SQLITE выбирает уникальные строки - PullRequest
0 голосов
/ 11 ноября 2019

У меня есть таблица, в которой строки выглядят как «дубликаты», но на самом деле их нет (у них разные даты).

Предположим, что каждая запись имеет столбец A, который должен быть уникальным. Однако из-за того, что этот столбец A может или не может появиться позже с обновленной информацией (с неизменным столбцом A), он больше не является уникальным, даже когда он должен быть.

Поэтому я хочу таблицу только с последней информацией. В настоящее время эта таблица содержит 500 тыс. Записей, однако «истинное» количество уникальных записей составляет менее половины.

Я пробовал

SELECT *
  FROM TABLE
    WHERE A = A  
    AND Date = (SELECT MAX(Date) from TABLE)
    ORDER BY DATE 

Однако это возвращает только 2 результата. Как мне этого добиться?

Ответы [ 3 ]

1 голос
/ 11 ноября 2019

Полагаю, если я понимаю, что вы написали, вы могли бы использовать: -

SELECT a,max(date), other FROM mytable GROUP BY a ORDER BY date;
  • обратите внимание, что другой столбец представляет другие столбцы (если есть)

  • Однако , другой столбец будет произвольным значением (из одного из сгруппированных столбцов), которое вполне может бытьтребуемое значение (в данном примере оно есть).

Согласно: -

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

SQL как понял SQLite - SELECT

Более правильно, чтобы исключить произвольное значение (sic) для другой столбец , вы можете использовать: -

SELECT 
    a /* will always be the same and isn't arbritary */,
    max(date) /* will be the maximum data */ AS date,
    (SELECT other FROM mytable WHERE a = m.a AND date = m.date) AS other
FROM mytable AS m /* AS m allows the outer query to be distinguished from the inner query */ 
GROUP BY a /* this effectivel removes duplicates on the a column */
ORDER BY date 
;
  • Пример, показанный ниже, дает тот же результат.

Пример: -

Использование следующего для заполнения таблицы некоторыми сгенерированными данными тестирования: -

CREATE TABLE IF NOT EXISTS mytable (a TEXT, date TEXT, other);
WITH cte(count,a,date,other) AS 
    (
        SELECT 1,1,date('now','+'||(random() % 30)||' days'),'other1'
        UNION ALL SELECT count+1,abs(random()) % 20,date('now','+'||(abs(random()) % 30)||' days'), 'other'||(count+1) FROM cte LIMIT 100

INSERT INTO mytable (a,date,other) SELECT a,date,other FROM cte 
;
SELECT * FROM mytable ORDER BY DATE DESC;

в этом случае: -

enter image description here

  • Выделенные строки - это те, которые должны быть извлечены.

Затем, после выполнения вышеизложенного, запускается следующее

SELECT * FROM mytable WHERE  a = a  AND  date = (SELECT MAX(date) FROM mytable);
SELECT * FROM mytable WHERE  /*a = a  AND*/  date = (SELECT MAX(date) FROM mytable);

/* Will only select 1 row per unique value of a BUT other will be an arbritary value not necessairlly the latest */
SELECT a,max(date), other FROM mytable GROUP BY a /* group by effectively display unique */; 

SELECT 
    a /* will always be the same and isn't arbritary */,
    max(date) /* will be the maximum data */ AS date,
    (SELECT other FROM mytable WHERE a = m.a AND date = m.date) AS other
FROM mytable AS m
GROUP BY a
;

Первые два результата показывают, что a = aничего не делает, так как всегда будет верно.

Тридцатный запрос создает (неупорядоченный): -

enter image description here

  • Отметьте галочкиприсваивается путем проверки значения другого из предыдущего результата.
    • В этом случае этот более короткий запрос работает нормально, даже если значения других являются произвольными значениями (на самом деле это не так, поскольку это зависит от того, как планировщик запросов обрабатывает запрос).

Четвертый, более правильный, дает те же результаты: -

enter image description here

Результат 2 (ваш оригинальный запрос) и 3 (оригинал без а = а) производят: -

enter image description here

и: -

enter image description here

1 голос
/ 11 ноября 2019

Другой подход, если вы используете несколько новую версию sqlite (3.25 или новее), используя оконную функцию row_number() для ранжирования групп с одинаковым значением a по дате и выбора первой:

WITH cte AS
 (SELECT a, date, row_number() OVER (PARTITION BY a ORDER BY date DESC) AS rn
  FROM yourtable)
SELECT a, date
FROM cte
WHERE rn = 1;

Одна важная вещь, на которую следует обратить внимание, поскольку я заметил, что вы упомянули, что другой ответ был медленным, это то, что для лучшего результата потребуется индекс на mytable(a, date DESC), а индекс на mytable(a, date) будет ускорятьсядругие ответы.

1 голос
/ 11 ноября 2019

Подзапрос на дату - правильная идея, но вы должны включить столбец A в подзапрос и связать его с основной таблицей. Я предпочитаю использовать явные объединения, а не встраивать подзапрос в оператор WHERE. В любом случае это обычно более эффективно.

SELECT TABLE.*
FROM TABLE INNER JOIN  
     (SELECT A, MAX(Date) AS MaxDate FROM TABLE GROUP BY A) AS latest
     ON TABLE.A = latest.A AND TABLE.date = latest.MaxDate 
ORDER BY A, date

Или даже лучше, я предпочитаю синтаксис CTE (Common Table Expression), так как он упрощает чтение отдельных запросов:

WITH latest AS (
    SELECT A, MAX(Date) AS MaxDate
    FROM TABLE 
    GROUP BY A
)
SELECT TABLE.*
FROM TABLE INNER JOIN latest
     ON TABLE.A = latest.A AND TABLE.date = latest.MaxDate 
ORDER BY TABLE.A, TABLE.date

Сравнение с другим ответом

Ответ от MikeT основан на нестандартной функции sqlite. Это само по себе, если вы знаете, что решение несовместимо с другими механизмами / серверами баз данных и диалектами SQL.

Следующая возможная ошибка действительно зависит от ваших фактических данных и схемы таблиц (ни одна из которыхВы поделились в деталях вопроса). Если ваши данные допускают несколько строк с одинаковой датой для одного значения столбца A, то условий в вашем вопросе недостаточно для окончательного удаления всех дубликатов. Вам нужно будет указать другой столбец, с помощью которого можно разрешить оставшиеся дубликаты, но ваш вопрос снова этого не сделал.

Однако в ходе тестирования я обнаружил, что мое решение позволяет оставить неразрешенные дубликаты в результатах. Решение MikeT устраняет все дубликаты, но делает это путем произвольного исключения одного из этих дубликатов. Есть способы исправить любое решение, чтобы точно выбрать, какой дубликат сохранить, но я даже не буду пытаться это делать, пока вы не опубликуете фактические данные и схему таблицы, так что мой ответ - не просто предположение. Я рад, что мой ответ был полезен до сих пор, но вам нужно лучше понять ваши данные (чем раскрыть в вопросе), чтобы убедиться, какое решение на самом деле лучше.

Бонус

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

WITH latest AS (
       SELECT A, MAX(Date) AS MaxDate
       FROM TABLE 
       GROUP BY A
    ),
    firstResults AS (
       SELECT TABLE.*
       FROM TABLE INNER JOIN latest
            ON TABLE.A = latest.A AND TABLE.date = latest.MaxDate 
       ORDER BY TABLE.A, TABLE.date
    )
SELECT otherTable.*
FROM firstResults JOIN otherTable
     ON firstResults.A = otherTable.A
WHERE somecondition = 'foobar'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...