ВЫБОР «первой» (как определено ORDER BY) строки из почти дублированных строк (как определено GROUP BY, HAVING, COUNT) в SQLite - PullRequest
5 голосов
/ 12 сентября 2011

У меня есть проблема, которая немного за мной (я действительно ужасно рада, что я бета) с участием дубликатов (так что GROUP BY, HAVING, COUNT), которые усугубляются сохранением решения в пределахстандартные функции, которые пришли с SQLite.Я использую модуль sqlite3 из Python.

Пример рабочих таблиц, столбцы:

* ID: integer, auto-incrementing
* ColA: integer
* ColB: varchar(20)
* UserType: varchar(20)
* LoadMe: Boolean   

(Да, типы данных SQLite являются номинальными)

Моя таблица данных,Рабочие с самого начала выглядят так:

ID  ColA  ColB  UserType  LoadMe
1   1     a     Alpha     0
2   1     b     Beta      0
3   2     a     Alpha     0
4   2     a     Beta      0
5   2     b     Delta     0
6   2     b     Alpha     0
7   1     a     Delta     0
8   1     b     Epsilon   0 
9   1     c     Gamma     0
10  4     b     Delta     0
11  5     a     Alpha     0
12  5     a     Beta      0
13  5     b     Gamma     0
14  5     a     Alpha     0

Я хотел бы включить для загрузки на грузовики на новом заводе всех рабочих, которые имеют уникальные комбинации между ColA и ColB.Для тех дубликатов (двойников, триплетов и т. Д., Возможно, с помощью процесса Бокановского), где уникальные комбинации ColA и ColB имеют более одного работника, я хотел бы выбрать только один из каждого набора дубликатов.Чтобы усложнить задачу, я хотел бы дополнительно сделать выбор по одному из каждого набора дубликатов на основе UserType в некоторой форме ORDER BY.Я могу выбрать первый «дубликат» с UserType «Alpha», чтобы работать со страшно умной проблемой, или ORDER BY UserType DESC, чтобы я мог оформить заказ на черные туники для самых маленьких из рабочих.

Вы можете видеть, что идентификаторы 9, 10 и 13 имеют уникальные комбинации ColA и ColB и их легче всего идентифицировать.Однако комбинации 1-a, 1-b, 2-a, 2-b и 5-a содержат дубликаты.

Мой текущий процесс в его нынешнем виде:

0) Каждый приходит с уникальным идентификационным номером.Это делается при рождении.

1) SET всем работникам до LoadMe = 1.

UPDATE Workers
SET LoadMe = 1

2) Найдите мои дубликаты на основе их сходства в двух столбцах (GROUP BY ColA,ColB):

SELECT Wk1.*
FROM Workers AS Wk1
INNER JOIN (
    SELECT ColA, ColB
    FROM Workers
    GROUP BY ColA, ColB
    HAVING COUNT(*) > 1
) AS Wk2
ON Wk1.ColA = Wk2.ColA
AND Wk1.ColB = Wk2.ColB
ORDER BY ColA, ColB

3) Устанавливать все мои дубликаты в LoadMe = 0.

UPDATE Workers
SET LoadMe = 0
WHERE ID IN (
    SELECT Wk1.ID
    FROM Workers AS Wk1
    INNER JOIN (
        SELECT ColA, ColB
        FROM Workers
        GROUP BY ColA, ColB
        HAVING COUNT(*) > 1
    ) AS Wk2
    ON Wk1.ColA = Wk2.ColA
    AND Wk1.ColB = Wk2.ColB
)

4) Для каждого набора дубликатов в моем GROUP BY, ORDER edBY UserType, SELECT только один, первый в списке, имеет LoadMe SET до 1.

Эта таблица будет выглядеть так:

ID  ColA  ColB  UserType  LoadMe
1   1     a     Alpha     1
2   1     b     Beta      1
3   2     a     Alpha     1
4   2     a     Beta      0
5   2     b     Delta     0
6   2     b     Alpha     1
7   1     a     Delta     0
8   1     b     Epsilon   0
9   1     c     Gamma     1
10  4     b     Delta     1
11  5     a     Alpha     1
12  5     a     Beta      0
13  5     b     Gamma     1
14  5     a     Alpha     0

ORDER edBY ColA, ColB, UserType, затем ID и разбитые по столбцам GROUP BY (и, наконец, для ясности) эти данные могут выглядеть так:

ID  ColA  ColB  UserType  LoadMe
1   1     a     Alpha     1
7   1     a     Delta     0

2   1     b     Beta      1
8   1     b     Epsilon   0

9   1     c     Gamma     1

3   2     a     Alpha     1
4   2     a     Beta      0

6   2     b     Alpha     1
5   2     b     Delta     0

10  4     b     Delta     1

11  5     a     Alpha     1
14  5     a     Alpha     0
12  5     a     Beta      0

13  5     b     Gamma     1

Я запутался впоследний шаг и почувствуй себя эпсилон-минус полумесяцем.Ранее я вытаскивал дубликаты из базы данных в пространство программы и работал в Python, но такая ситуация возникает нередко, и я хотел бы более навсегда решить эту проблему.

1 Ответ

1 голос
/ 10 июня 2012

Я бы хотел немного решить эту проблему.Первый шаг - определить уникальные пары ColA, ColB:

SELECT ColA,ColB FROM Workers GROUP BY ColA,ColB

Теперь для каждой из этих пар вы хотите найти запись с наивысшим приоритетом.Объединение не будет работать, потому что у вас будет несколько записей для каждой уникальной пары, но подзапрос будет работать:

SELECT ColA,ColB,
    (SELECT id FROM Workers w1 
    WHERE w1.ColA=w2.ColA AND w1.ColB=w2.ColB 
    ORDER BY UserType LIMIT 1) AS id
FROM Workers w2 GROUP BY ColA,ColB;

Вы можете изменить предложение ORDER BY в подзапросе, чтобы управлять приоритетом.LIMIT 1 гарантирует, что для каждого подзапроса есть только одна запись (в противном случае sqlite вернет последнюю запись, которая соответствует предложению WHERE, хотя я не уверен, что это гарантировано).

Результат этогозапрос представляет собой список записей для загрузки с ColA, ColB, id.Я бы, вероятно, поработал непосредственно с этим и избавился бы от LoadMe, но если вы хотите сохранить его, вы можете сделать это:

BEGIN TRANSACTION;
UPDATE Workers SET LoadMe=0;
UPDATE Workers SET LoadMe=1
WHERE id IN (SELECT 
    (SELECT id FROM Workers w1 
    WHERE w1.ColA=w2.ColA AND w1.ColB=w2.ColB 
    ORDER BY UserType LIMIT 1) AS id
    FROM Workers w2 GROUP BY ColA,ColB);
COMMIT;

, который очищает флаг LoadMe, а затем устанавливает его в 1 для каждого иззаписи, возвращаемые нашим последним запросом.Транзакция гарантирует, что все это происходит или завершается неудачей в один шаг и никогда не оставляет ваши поля LoadMe в несогласованном состоянии.

...