Упорядочить по дате, группируя совпадения по другому столбцу - PullRequest
0 голосов
/ 12 января 2019

У меня есть этот запрос

SELECT *, COUNT(app.id) AS totalApps FROM users JOIN app ON app.id = users.id
  GROUP BY app.id ORDER BY app.time DESC LIMIT ?

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

У меня проблема в том, что группировка выполняется до упорядочения по дате, поэтому я получаю очень старые результаты. Но мне нужна группировка, чтобы получить отдельных пользователей, потому что у каждого пользователя может быть несколько «приложений» ... Есть ли другой способ добиться этого?


Пользователи таблицы:

id TEXT PRIMARY KEY

Настольное приложение:

id TEXT
time DATETIME
FOREIGN KEY(id) REFERENCES users(id)

В моем запросе SELECT я хочу получить список пользователей, упорядоченный по столбцу app.time. Но поскольку у одного пользователя может быть связано несколько записей приложения, я мог получить дублированных пользователей, поэтому я использовал GROUP BY. Но тогда порядок испорчен

Ответы [ 5 ]

0 голосов
/ 12 января 2019

Основная проблема заключается в том, что SELECT является агрегированным запросом, поскольку он содержит предложение GROUP BY: -

Существует два типа простого оператора SELECT - агрегат и неагрегированные запросы. Простой оператор SELECT - это совокупный запрос если он содержит предложение GROUP BY или один или несколько агрегатов функции в наборе результатов.

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

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

Если оператор SELECT является агрегированным запросом без GROUP BY предложение, затем оценивается каждое статистическое выражение в наборе результатов. один раз по всему набору данных. Каждое неагрегированное выражение в Набор результатов оценивается один раз для произвольно выбранной строки набор данных. Одна и та же произвольно выбранная строка используется для каждого неагрегированное выражение. Или, если набор данных содержит ноль строк, то каждое неагрегированное выражение оценивается по строке, состоящей из полностью из значений NULL.

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

Поэтому приходится извлекать необходимые значения, используя агрегированное выражение, например, max (app.time). Тем не менее, вы не можете ЗАКАЗАТЬ по этому значению (точно не знаю, почему оно, вероятно, наследуемо в аспекте эффективности)

ОДНАКО

Что вы можете сделать, это использовать запрос для построения CTE, а затем сортировать без использования агрегатов.

Рассмотрим следующее, которое, как мне кажется, имитирует вашу проблему: -

DROP TABLE IF EXISTS users;
DROP TABLE If EXISTS app;

CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT);
INSERT INTO users (username) VALUES ('a'),('b'),('c'),('d');

CREATE TABLE app (the_id INTEGER PRIMARY KEY, id INTEGER, appname TEXT, time TEXT);
INSERT INTO app (id,appname,time) VALUES
    (4,'app9',721),(4,'app10',7654),(4,'app11',11),
        (3,'app1',1000),(3,'app2',7),
        (2,'app3',10),(2,'app4',101),(2,'app5',1),
        (1,'app6',15),(1,'app7',7),(1,'app8',212),
        (4,'app9',721),(4,'app10',7654),(4,'app11',11),
        (3,'app1',1000),(3,'app2',7),
        (2,'app3',10),(2,'app4',101),(2,'app5',1),
        (1,'app6',15),(1,'app7',7),(1,'app8',212)
    ;
    SELECT * FROM users;
    SELECT * FROM app;

    SELECT username 
      ,count(app.id) 
      , max(app.time) AS latest_time
        , min(app.time) AS earliest_time
    FROM users JOIN app ON users.id = app.id 
    GROUP BY users.id
    ORDER BY max(app.time)
    ;

В результате: -

enter image description here

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

Завершение этого в CTE может исправить это, например. : -

WITH cte1 AS 
(
    SELECT username 
        ,count(app.id) 
        , max(app.time) AS latest_time
        , min(app.time) AS earliest_time
    FROM users JOIN app ON users.id = app.id 
    GROUP BY users.id
)
SELECT * FROM cte1 ORDER BY cast(latest_time AS INTEGER) DESC;

и сейчас: -

enter image description here

  • Обратите внимание, что для моего удобства вместо целых чисел были использованы простые целые числа.
0 голосов
/ 12 января 2019

Вы можете использовать оконный COUNT:

SELECT *, COUNT(app.id) OVER(PARTITION BY app.id) AS totalApps 
FROM users 
JOIN app 
  ON app.id = users.id
ORDER BY app.time DESC
LIMIT ?
0 голосов
/ 12 января 2019

Может быть, вы могли бы использовать?

SELECT DISTINCT

Подробнее здесь: https://www.w3schools.com/sql/sql_distinct.asp

0 голосов
/ 12 января 2019

Попробуйте группировать по идентификатору и времени, а затем упорядочить по времени.

select ... 
group by app.id desc, app.time

Я предполагаю, что идентификатор уникален в таблице приложения. и как вы назначаете идентификатор? может быть, вам достаточно заказать по id desc

0 голосов
/ 12 января 2019

Поскольку вам нужна самая новая дата в каждой группе, вы можете просто MAX их:

SELECT
  *,
  COUNT(app.id) AS totalApps,
  MAX(app.time) AS latestDate
FROM users
  JOIN app ON app.id = users.id
GROUP BY app.id
ORDER BY latestDate DESC
LIMIT ?
...