Объединяйте информацию из одной таблицы в другую без двойного объединения и применяйте T-SQL - PullRequest
2 голосов
/ 09 мая 2019

Положение:

У меня есть две таблицы. # t1 имеет логины и электронные письма. # t2 имеет страну, связанную с каждым письмом. Я хотел бы присоединить информацию от # t2 к # t1 без необходимости присоединяться к ней дважды. Присоединение к нему только один раз во внутреннем или внешнем запросе нарушит логику cross apply. Мой текущий запрос использует перекрестное применение для получения скользящей информации как таковой (данные скрипта ниже):

SELECT DISTINCT CAST(logins AS DATE) AS Dates,
    count(distinct d.email) AS DAU,
    count(distinct m.MAU) AS MAU
FROM #t1 d
CROSS APPLY (
            SELECT CAST(m.logins as date) as dates, m.email AS MAU
            FROM #t1 m
            WHERE m.logins BETWEEN d.logins and DATEADD(dd, 30, d.logins) 
            ) m
group by CAST(logins as date)

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

SELECT distinct CASt(logins AS DATE) AS Dates,
    #t2.country,
    count(distinct d.email) AS DAU,
    count(distinct m.MAU) AS MAU
FROM #t1 d
inner join #t2 on d.email=#t2.email 
CROSS APPLY (
            SELECT cast(m.logins as date) as dates, m.email AS MAU, country.country AS country
            FROM #t1 m
            inner join #t2 country on m.email=country.email         
            WHERE m.logins BETWEEN d.logins and DATEADD(dd, 30, d.logins)  
            and #t2.country = country.country
            ) m
group by cast(logins as date), #t2.country


+-------------+---------+-----+-----+
|    Dates    | country | DAU | MAU |
+-------------+---------+-----+-----+
|  2019-04-01 | france  |   1 |   2 |
|  2019-04-02 | france  |   1 |   2 |
|  2019-04-03 | france  |   1 |   2 |
|  2019-04-10 | france  |   1 |   1 |
|  2019-04-03 | italie  |   2 |   2 |
+-------------+---------+-----+-----+

Цель:

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

Результат должен выглядеть следующим образом (вывод из второго запроса выше):

  • DAU: сколько разных регистраций в каждой стране произошло в день 'x'
  • MAU: сколько разных логинов в каждой стране произошло между днем ​​'x' и через 30 дней.

Fiddle:

create table #t1 (email varchar(max), logins datetime)
insert into #t1 values 
('aa@gmail.com', '2019-04-01 00:00:00.000'),
('aa@gmail.com', '2019-04-02 00:00:00.000'), 
('aa@gmail.com', '2019-04-03 00:00:00.000'), 
('zz@gmail.com', '2019-04-10 00:00:00.000'), 

('cc@gmail.com', '2019-04-03 00:00:00.000'), 
('dd@gmail.com', '2019-04-03 00:00:00.000'), 
('dd@gmail.com', '2019-04-03 00:00:00.000')  

create table #t2 (country varchar(max), email varchar(max))
insert into #t2 values 
('france', 'aa@gmail.com'),
('france', 'zz@gmail.com'),
('italie', 'cc@gmail.com'),
('italie', 'dd@gmail.com')

1 Ответ

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

Обновление

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

В моей тестовой среде я генерировал ваши таблицы в виде постоянных таблиц и сначала заполнил # t2 (я назвал это emailLocation) с 100 000 уникальных адресов электронной почты, распределенных по 206 странам.Вторая таблица (loginRecord) была заполнена 2 000 000 случайных записей, разбросанных по периодам с 01.01.2008 по 31.12.2009.Обе эти таблицы проиндексированы.

Ниже приведен запрос, который, как я сказал, будет медленнее (это не так).Основное различие в этом состоит в том, что я фильтрую даты в CTE, чтобы уменьшить набор данных.В моей среде это выполняется за 20 секунд и возвращает 48 410 строк.Я не проверял, сколько времени потребуется, чтобы вернуть весь набор, но попытка того же самого CTE с самостоятельным объединением длилась 10 минут, прежде чем я его убил.

WITH joined AS
(
    SELECT
        t1.logins AS dates,
        t1.email,
        t2.country
        FROM loginRecord t1
        JOIN dbo.emailLocation t2 ON t2.email = t1.email
        WHERE t1.logins > GETDATE()
)
SELECT 
    dates,
    country,
    COUNT(DISTINCT(email)) AS DAU,
    (SELECT COUNT(DISTINCT(email)) FROM joined WHERE country = j.country AND dates BETWEEN j.dates AND DATEADD(DAY,30,j.dates)) AS MAU
FROM joined j
GROUP BY j.dates, j.country
ORDER BY country, dates

--- оригинальный ответ

Такое ощущение, что вы застряли на логике cross apply.Вот два варианта, которые не используют cross apply.Оба используют CTE, чтобы получить хорошую чистую группировку ваших временных таблиц, тогда первый вариант - это коррелированный подзапрос (blech), а второй - самостоятельное соединение.

Rextester здесь: https://rextester.com/AVJS76389

WITH joined AS
(
    SELECT 
        t1.logins AS dates,
        t1.email,
        t2.country
        FROM #t1 t1
        JOIN #t2 t2 ON t2.email = t1.email
)
SELECT 
    dates,
    country,
    COUNT(DISTINCT(email)) AS DAU,
    (SELECT COUNT(DISTINCT(email)) FROM joined WHERE country = j.country AND dates BETWEEN j.dates AND DATEADD(DAY,30,j.dates)) AS MAU
FROM joined j
GROUP BY j.dates, j.country;



WITH joined AS
(
    SELECT 
        t1.logins AS dates,
        t1.email,
        t2.country
        FROM #t1 t1
        JOIN #t2 t2 ON t2.email = t1.email
)
SELECT 
    j1.dates,
    j1.country,
    COUNT(DISTINCT(j1.email)) AS DAU,
    COUNT(DISTINCT(j2.email)) AS MAU
FROM joined j1
JOIN joined j2
    ON j1.country = j2.country
    AND j2.dates BETWEEN j1.dates AND DATEADD(DAY,30,j1.dates)
GROUP BY j1.dates, j1.country 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...