Объедините количество таблиц в mariadb и найдите отличное значение на основе отметки времени - PullRequest
0 голосов
/ 20 мая 2019

Мне нужно сделать поиск по нескольким таблицам базы данных. Поскольку все таблицы имеют одинаковую структуру (одинаковые столбцы, они на самом деле являются архивированными данными для одной и той же таблицы), я могу использовать UNION ALL, чтобы объединить их все.

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

Я просмотрел документацию и некоторое время гуглил и не смог найти удовлетворительного решения с MySQL.

Единственный способ, которым я могу придумать, - написать некоторый внешний код для поиска значений с помощью UNION ALL для каждого имени пользователя. И запустите запрос 1 к 1. Зная, что в базе данных указано 100k различных имен пользователей, это займет довольно много времени. Я уже запустил этот скрипт, однако он кажется действительно неэффективным и тратит ресурсы.

SELECT * FROM table1 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table2 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table3 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table4 WHERE `USERNAME` LIKE 'USERNAMEXXX'
ORDER BY TIME_STAMP ASC
LIMIT 1

Выше SQL-запрос дает мне то, что я хочу для каждого имени пользователя, но мне нужно повторить его для каждого отдельного имени пользователя.

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

Есть ли способ добиться этого с помощью собственного SQL, без использования внешних сценариев? Нечто, объединяющее различное, объединяющее все и упорядоченное по временной отметке asc limit 1.

Ответы [ 2 ]

1 голос
/ 20 мая 2019

В MySQL 8+ вы можете использовать оконные функции:

SELECT t.*
FROM (SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY username ORDER BY timestamp) as seqnum
      FROM ((SELECT t.* FROM table1 t) UNION ALL
            (SELECT t.* FROM table2 t) UNION ALL
            (SELECT t.* FROM table3 t) UNION ALL
            (SELECT t.* FROM table4 t)
           ) t
     ) t
WHERE seqnum = 1;

В более ранних версиях вы можете использовать переменные:

SELECT t.*
FROM (SELECT t.*,
             (@rn := IF(@u = username, @rn + 1,
                        IF(@u := username, 1, 1)
                       )
             ) as seqnum
             ROW_NUMBER() OVER (PARTITION BY username ORDER BY timestamp) as seqnum
      FROM ((SELECT t.* FROM table1 t) UNION ALL
            (SELECT t.* FROM table2 t) UNION ALL
            (SELECT t.* FROM table3 t) UNION ALL
            (SELECT t.* FROM table4 t)
            ORDER BY username, timestamp
           ) t CROSS JOIN
           (SELECT @u := '', @rn := 0) params
     ) t
WHERE seqnum = 1;

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

SELECT username, MIN(timestamp),
FROM ((SELECT t.* FROM table1 t) UNION ALL
      (SELECT t.* FROM table2 t) UNION ALL
      (SELECT t.* FROM table3 t) UNION ALL
      (SELECT t.* FROM table4 t)
     ) t
GROUP BY username;
1 голос
/ 20 мая 2019

Если я правильно понял ваш вопрос

Вы можете использовать min для самой ранней отметки времени min (my_time_stamp) и UNION (для возврата различного)

select  username, min(my_time_stamp)
from  table1 
group  by username 
union  
select  username, min(my_time_stamp)
from  table2 
group  by username 
union
select  username, min(my_time_stamp)
from  table3 
group  by username 
....

union
select  username, min(my_time_stamp)
from  tableN
group  by username 

order by username  
...