Group By игнорирует сортировку в подзапросе - PullRequest
1 голос
/ 28 февраля 2020

Внизу имеется версия TLDR.

Примечание. Мое текущее решение основано на предложенном решении в здесь, в этом вопросе (предложенном в самом тексте вопроса) , однако это не работает для меня, даже если это работает для этого человека. Так что я не уверен, как с этим справиться, потому что вопрос кажется дубликатом, но ответ, который там дан, не работает для меня. Поэтому я думаю, что-то должно быть по-другому для меня. Если кто-то может сказать мне, как правильно с этим справиться, я открыт для слушания.

У меня есть такая таблица:

scope_id    key_id    value
0           0         0_0
0           1         0_1
1           0         1_0
2           0         2_0
2           1         2_1

Области имеют иерархию, где область действия 0 является родителем области 2, а область 2 является родителем области 1. (с целью не отсортированы, их идентификаторы являются UUID, только для чтения чисел здесь)

Мой вариант использования заключается в том, что я хочу получить значение множественного ключи в определенной c области (область 1). Однако, если для области 1 не определено значение, я подойдет со значением его родителя (область 2) и, наконец, если в области 2 также нет значения, я бы взял значение из его родителя, область 0. Поэтому, если возможно, я хочу получить значение из области 1, если оно не имеет значения, то из области 2 и, наконец, я пытаюсь получить значение из области 0. (Области являются древовидной структурой, поэтому каждая область может иметь не более одного родителя). Однако родитель может иметь несколько дочерних элементов.)

Так что в приведенном выше примере, если я хочу получить значение ключа 0 в области 1, я хотел бы получить 1_0, поскольку ключ определен в объем. Если я хочу получить значение ключа 1 в области видимости 1, я хотел бы получить 2_1, поскольку в области действия 1 не определено значение, но в родительской области действия 2 оно есть. И, наконец, если я хочу получить значения ключей 0 и 1 в области видимости 1, я хочу получить 1_0 и 2_1.

В настоящее время это решается путем создания 3 отдельных SQL запросов и объединения их в код. Это работает хорошо и достаточно быстро, но я хочу посмотреть, будет ли это быстрее с одним запросом SQL. Я предложил следующий запрос (на основе обновления в тексте вопроса здесь ):

SELECT * 
FROM (
    SELECT * 
    FROM test
    WHERE key_id IN (0, 1)
        AND scope_id IN (1 , 2, 0)
    ORDER BY FIELD(scope_id, 1 , 2, 0)
) t1
GROUP BY t1.key_id;

Внутренний подзапрос сначала находит все ключи, на которые я хочу посмотреть, и проверяет, они находятся в области, которую я хочу посмотреть, или в родительской области. Затем я заказываю прицелы, чтобы сначала был ребенок, потом родитель, потом дедушка. Теперь я ожидаю, что group by оставит значение первой найденной строки, так что, надеюсь, дочерний элемент (область 1). Однако это не работает. Вместо этого используется первое значение, основанное на фактической таблице.

TLDR

При группировании с GROUP BY в запросе выше, почему порядок, определенный запросом ORDER BY, игнорируется? Вместо этого при группировании берется первое значение, основанное на исходной таблице.

Используя этот код, вы можете попробовать сами:

# this group by doesn't work with strict mode
SET sql_mode = '';
CREATE TABLE IF NOT EXISTS test(
    scope_id int,
    key_id int,
    `value` varchar(20),
    PRIMARY KEY (scope_id, key_id)
);
INSERT IGNORE INTO test values 
    (0, 0, "0_0"),
    (1, 0, "1_0"),
    (2, 0, "2_0"),
    (2, 1, "2_1"),
    (0, 1, "0_1");

SELECT * 
FROM (
    SELECT * 
    FROM test
    WHERE key_id IN (0, 1)
        AND scope_id IN (1 , 2, 0)
    ORDER BY FIELD(scope_id, 1 , 2, 0)
) t1
GROUP BY t1.key_id;
# expected result are the rows that contain value 1_0 and 2_1

Ответы [ 2 ]

1 голос
/ 28 февраля 2020

Я понимаю ваш вопрос как вариант с наибольшим числом групп.

В этой ситуации вам не следует думать агрегация , но фильтрация .

Вы можете решить ее с помощью коррелированного подзапроса, который выбирает первый доступный scope_id за key_id:

select t.*
from test t
where t.scope_id = (
    select t1.scope_id 
    from test t1 
    where t1.key_id = t.key_id
    order by field(scope_id, 1, 2, 0)
    limit 1
)

Для производительности требуется индекс на (key_id, scope_id).

Демонстрация на DB Fiddle :

scope_id | key_id | value
-------: | -----: | :----
       1 |      0 | 1_0  
       2 |      1 | 2_1  
0 голосов
/ 29 февраля 2020

Это получит то, что вы хотите. Используйте номер строки, чтобы эффективно «сохранить» ваш заказ для следующего раздела запроса.

MySQL 8.0 или новее:

SELECT * 
FROM (
    SELECT *, ROW_NUMBER() rank
    FROM test
    WHERE key_id IN (0, 1)
        AND scope_id IN (1 , 2, 0)
    ORDER BY FIELD(scope_id, 1 , 2, 0)
) t1
GROUP BY t1.key_id
order by rank;

MySQL 5.7 или старше:

SET @row_num = 0;
SELECT * 
FROM (
    SELECT *, @row_num := @row_num + 1 rank
    FROM test
    WHERE key_id IN (0, 1)
        AND scope_id IN (1 , 2, 0)
    ORDER BY FIELD(scope_id, 1 , 2, 0)
) t1
GROUP BY t1.key_id
ORDER BY rank;

Soap Поле: MySQL результаты вообще ужасно ненадежны в любом запросе, который имеет 1 или более столбцов в группе или совокупности, но не имеет всех столбцов в группе или совокупности .

...