Выберите значения, которые соответствуют различным условиям в разных строках?

Добавление к замечательному ответу @ Бернхарда

# original file was written with pretty-print inside a list
with open("pretty-printed.json") as jsonfile:
    js = json.load(jsonfile)      

# write a new file with one object per line
with open("flattened.json", 'a') as outfile:
    for d in js:
        json.dump(d, outfile)
        outfile.write('\n')
29
задан AdamMc331 23 December 2014 в 22:23
поделиться

5 ответов

SELECT userid
FROM UserRole
WHERE roleid IN (1, 2, 3)
GROUP BY userid
HAVING COUNT(DISTINCT roleid) = 3;
<час>

любому читающему это: мой ответ прост и прост, и получил 'принятое' состояние, но пойдите, читает ответ данный @cletus. Это имеет намного лучшую производительность.

<час>

Justing, думающий вслух, другой способ записать самосоединение, описанное @cletus:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid
JOIN userrole t3 ON t2.userid = t3.userid
WHERE (t1.roleid, t2.roleid, t3.roleid) = (1, 2, 3);

Это могло бы быть легче считать для Вас, и MySQL поддерживает сравнения кортежей как этот. MySQL также знает, как использовать закрывающие индексы разумно для этого запроса. Просто выполните его до EXPLAIN и посмотрите "Используя индекс" в примечаниях для всех трех таблиц, что означает, что это читает индекс и не должно даже касаться строк данных.

я выполнил этот запрос более чем 2,1 миллиона строк (дамп данных в июле Переполнения стека для PostTags) использование MySQL 5.1.48 на моем MacBook, и это возвратило результат через 1,08 секунды. На достойном сервере с достаточной памятью, выделенной innodb_buffer_pool_size, это должно быть еще быстрее.

26
ответ дан Community 24 December 2014 в 08:23
поделиться
  • 1
    Это работало на меня и сохранило меня ТОННА времени и потенциальных ошибок. Было неясно на том, какой пишут сценарий мастера, генерировал бы. Это действительно генерирует сценарий CREATE TABLE при выборе таблицы. Этот ответ был downvoted ранее. Какая-либо конкретная причина, почему? – jward01 24 November 2016 в 08:05

Принимая идентификатор пользователя, roleid содержатся в уникальном индексе (значение, что не может быть 2 записей где идентификатор пользователя = x и roleid = 1

select count(*), userid from t
where roleid in (1,2,3)
group by userid
having count(*) = 3
3
ответ дан jmucchiello 24 December 2014 в 08:23
поделиться
  • 1
    Спасибо! Я знал, что был способ иметь все создавание к сценариям в одном месте, Ваш ответ указал на меня для обнаружения где. – Tschallacka 23 August 2017 в 20:02

Если Вам нужен какой-либо вид общности здесь (различные комбинации с 3 ролями или различные n-ролевые комбинации)... Я предложил бы, чтобы Вы использовали немного систему маскирования для своих ролей и использовали побитовые операторы для выполнения запросов...

-6
ответ дан Jason Punyon 24 December 2014 в 08:23
поделиться

Хорошо, я получил downvoted на этом так, я решил протестировать его:

CREATE TABLE userrole (
  userid INT,
  roleid INT,
  PRIMARY KEY (userid, roleid)
);

CREATE INDEX ON userrole (roleid);

Выполнение это:

<?php
ini_set('max_execution_time', 120); // takes over a minute to insert 500k+ records 

$start = microtime(true);

echo "<pre>\n";
mysql_connect('localhost', 'scratch', 'scratch');
if (mysql_error()) {
    echo "Connect error: " . mysql_error() . "\n";
}
mysql_select_db('scratch');
if (mysql_error()) {
    echo "Selct DB error: " . mysql_error() . "\n";
}

$users = 200000;
$count = 0;
for ($i=1; $i<=$users; $i++) {
    $roles = rand(1, 4);
    $available = range(1, 5);
    for ($j=0; $j<$roles; $j++) {
        $extract = array_splice($available, rand(0, sizeof($available)-1), 1);
        $id = $extract[0];
        query("INSERT INTO userrole (userid, roleid) VALUES ($i, $id)");
        $count++;
    }
}

$stop = microtime(true);
$duration = $stop - $start;
$insert = $duration / $count;

echo "$count users added.\n";
echo "Program ran for $duration seconds.\n";
echo "Insert time $insert seconds.\n";
echo "</pre>\n";

function query($str) {
    mysql_query($str);
    if (mysql_error()) {
        echo "$str: " . mysql_error() . "\n";
    }
}
?>

Вывод:

499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.

, Который добавляет 500 000 случайных комбинаций пользовательской роли и существуют приблизительно 25 000, которые соответствуют выбранным критериям.

Первый запрос:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Время запроса: 0.312 времени запроса s

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

: 0,016 s

Правильно. Версия соединения, которую я предложил, в двадцать раз быстрее, чем совокупная версия.

Жаль, но я делаю это для жизни и работы в реальном мире и в реальном мире, мы тестируем SQL, и результаты выступают за себя.

причина этого должна быть довольно ясной. Агрегатный запрос масштабируется в стоимости с размером таблицы. Каждая строка обработана, агрегирована и фильтрована (или не) через HAVING пункт. Версия соединения будет (использование индекса) выбирают подмножество пользователей на основе данной роли, затем проверяют что подмножество по второй роли и наконец что подмножество против третьей роли. Каждый выбор алгебра отношений условия) работает над все больше небольшим подмножеством. От этого можно завершить:

производительность версии соединения становится еще лучше с более низким падением соответствий.

, Если было только 500 пользователей (из 500k образца выше), который имел три установленных роли, версия соединения станет значительно быстрее. Совокупная версия не будет (и любое повышение производительности является результатом переноса 500 пользователей вместо 25k, который версия соединения, очевидно, получает также).

мне было также любопытно видеть, как реальная база данных (т.е. Oracle) будет иметь дело с этим. Таким образом, я в основном повторил то же осуществление на Oracle, XE (работающий на той же настольной машине Windows XP как MySQL от предыдущего примера) и результаты почти идентичен.

Соединения, кажется, осуждены, но как я продемонстрировал, агрегатные запросы могут быть порядком величины медленнее.

Обновление: приблизительно после обширное тестирование , изображение более сложно, и ответ будет зависеть от Ваших данных, Вашей базы данных и других факторов. Мораль истории является тестом, тестом, тестом.

114
ответ дан Carl Manaster 24 December 2014 в 08:23
поделиться

Классический способ сделать это - рассматривать это как проблему реляционного деления.

На английском языке: выберите тех пользователей, для которых не пропущено ни одно из желаемых значений roleid.

Я предполагаю, что у вас есть таблица Users, на которую ссылается таблица UserRole, и я предполагаю, что желаемые значения roleid находятся в таблица:

create table RoleGroup(
  roleid int not null,
  primary key(roleid)
)
insert into RoleGroup values (1);
insert into RoleGroup values (2);
insert into RoleGroup values (3);

Я также предполагаю, что все соответствующие столбцы не имеют значения NULL, поэтому нет никаких сюрпризов с IN или NOT EXISTS. Вот SQL-запрос, который выражает английский выше:

select userid from Users as U
where not exists (
  select * from RoleGroup as G
  where not exists (
    select R.roleid from UserRole as R
    where R.roleid = G.roleid
    and R.userid = U.userid
  )
);

Другой способ написать это

select userid from Users as U
where not exists (
  select * from RoleGroup as G
  where G.roleid not in (
    select R.roleid from UserRole as R
    where R.userid = U.userid
  )
);

Это может оказаться эффективным, а может и не оказаться эффективным, в зависимости от индексов, платформы, данных и т. Д. Поищите в Интернете "реляционное разделение" "и вы найдете много.

4
ответ дан 28 November 2019 в 00:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: