Mysql. Как выбрать пользователей из таблицы журнала, используя две разные текстовые записи - PullRequest
1 голос
/ 25 апреля 2019

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

| user_id | action            |
| 12345   | app: IOs          |
| 12345   | app_version: 2018 |
| 12346   | app: Android      |
| 12346   | app_version: 2019 |
| 12347   | app: Windows      |
| 12347   | app_version: 2019 |

Есть ли способ получить все идентификаторы пользователей, которые используют старые (2018) мобильные приложения?

Есть способ, которым я это сделал, но он неэффективен

SELECT 
     user_id
FROM 
    log
WHERE 
    action LIKE '%2018%'
AND 
    user_id IN (SELECT DISTINCT user_id FROM log WHERE(action LIKE '%IOs%' OR action LIKE '%Android%' ))
GROUP BY user_id

Этот запрос занял около получаса на производстве.

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

Ответы [ 3 ]

1 голос
/ 25 апреля 2019

Вы можете использовать агрегацию:

SELECT l.user_id
FROM log l
WHERE l.action LIKE '%2018%' OR
      l.action LIKE '%IOs%' OR
      l.action LIKE '%Android%'
GROUP BY l.user_id
HAVING SUM(l.action LIKE '%2018%') > 0 AND       -- at least one 2018
       SUM(l.action LIKE '%2018%') <> COUNT(*);  -- at least one other

К сожалению, для сравнения LIKE требуется сканирование таблицы log.Единственный способ обойти это - использовать полнотекстовый индекс.

Вы можете упростить логику до:

SELECT l.user_id
FROM log l
WHERE l.action REGEXP '2018|IOs|Android'
GROUP BY l.user_id
HAVING SUM(l.action LIKE '%2018%') > 0 AND       -- at least one 2018
       SUM(l.action LIKE '%2018%') <> COUNT(*);  -- at least one other

Я не уверен, что один REGEXP (незначительно)быстрее, чем три LIKE с или нет.

0 голосов
/ 25 апреля 2019

Вот мое решение с LEFT JOIN.Я понимаю, что у вас есть большая таблица журналов, поэтому она может быть не самой лучшей.Я также добавил еще несколько записей для тестирования:

В основном я использую LEFT JOIN для перемещения данных из столбцов в строки, чтобы можно было просто фильтровать с помощью WHERE.

скрипта SQL:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=9db538e59b3d265e4e8d8559762e79d4

WITH log_table AS (
      SELECT *
      FROM (VALUES (12345, 'app: iOS'),
                   (12345, 'app_version: 2018'),
                   (12346, 'app: Android'),
                   (12346, 'app_version: 2019'),
                   (12347, 'app: Windows'),
                   (12347, 'app_version: 2019'),
                   (12348, 'app: iOS'),
                   (12348, 'app_version: 2019'),
                   (12349, 'app: Android'),
                   (12349, 'app_version: 2018'),
                   (12350, 'app: Windows'),
                   (12350, 'app_version: 2018')
           ) v(user_id, action)
)
SELECT 
    L.user_id
FROM 
    log_table AS L 
    LEFT JOIN log_table AS L2 
         ON L.user_id = L2.user_id
WHERE (L.action LIKE '%iOS%' OR L.action LIKE '%Android%') AND L2.action LIKE '%2018%'

Результат: (выберите только те, у которых iOS или Android и версия 2018)

user_id
 12345
 12349
0 голосов
/ 25 апреля 2019

Вы можете использовать EXISTS:

SELECT l.*
FROM log l
WHERE EXISTS (SELECT 1 FROM log l1 WHERE l1.user_id = l.user_id AND l1.action LIKE '%2018%');
...