Следующая таблица истории пользователей содержит одну запись для каждого дня, когда данный пользователь заходил на веб-сайт (в 24-часовом периоде в формате UTC). В нем много тысяч записей, но только одна запись в день на пользователя. Если пользователь не заходил на веб-сайт в этот день, запись не создается.
Id UserId CreationDate ------ ------ ------------ 750997 12 2009-07-07 18: 42: 20.723 750998 15 2007-07-07 18:42: 20.927 751000 19 2009-07-07 18: 42: 22.283
Я ищу SQL-запрос к этой таблице с хорошей производительностью , который сообщает мне, какие идентификаторы пользователей обращались к веб-сайту в течение (n) непрерывных дней, не пропуская ни одного дня.
Другими словами, сколько пользователей имеют (n) записей в этой таблице с последовательными датами (на день до или на день после) ? Если в последовательности пропущен какой-либо день, последовательность прерывается и должна возобновиться снова с 1; мы ищем пользователей, которые прожили здесь непрерывное количество дней без пропусков.
Любое сходство между этим запросом и конкретным значком Stack Overflow , конечно же, чисто случайное .. :)
источник
Ответы:
Ответ очевиден:
РЕДАКТИРОВАТЬ:
Хорошо, вот мой серьезный ответ:
РЕДАКТИРОВАТЬ:
[Джефф Этвуд] Это отличное быстрое решение и заслуживает того, чтобы его приняли, но решение Роба Фарли также превосходно и, возможно, даже быстрее (!). Пожалуйста, проверьте это тоже!
источник
ON uh2.CreationDate >= uh1.CreationDate AND uh2.CreationDate < DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate) + @days, 0)
чтобы означать «Еще не на 31-й день позже». Также означает, что вы можете пропустить расчет @seconds.Как насчет (и убедитесь, что предыдущий оператор заканчивался точкой с запятой):
Идея состоит в том, что если у нас есть список дней (в виде числа) и row_number, то пропущенные дни немного увеличивают смещение между этими двумя списками. Итак, мы ищем диапазон с постоянным смещением.
Вы можете использовать «ORDER BY NumConsecutiveDays DESC» в конце этого или сказать «HAVING count (*)> 14» для порога ...
Я не тестировал это, просто списывая это с головы. Надеюсь, работает с SQL2005 и далее.
... и мне бы очень помог индекс по имени таблицы (UserID, CreationDate)
Отредактировано: Оказалось, что Offset - зарезервированное слово, поэтому вместо этого я использовал TheOffset.
Отредактировано: предложение использовать COUNT (*) очень актуально - я должен был сделать это в первую очередь, но на самом деле не думал. Раньше вместо этого использовался dateiff (day, min (CreationDate), max (CreationDate)).
обкрадывать
источник
Если вы можете изменить схему таблицы, я бы предложил добавить
LongestStreak
в таблицу столбец, в котором вы должны установить количество последовательных дней, заканчивающихся наCreationDate
. Это легко обновлять таблицу во время входа (подобно тому , что вы делаете уже, если ни одна строка не существует на текущий день, вы будете проверять, существует ли какой - либо строка за предыдущий день. Если это правда, вы будете увеличиватьLongestStreak
в новая строка, в противном случае вы установите значение 1.)Запрос будет очевиден после добавления этого столбца:
источник
Какой-нибудь красиво выразительный SQL в духе:
Предполагая, что у вас есть определенная пользователем агрегатная функция чего-то вроде (будьте осторожны, это ошибочно):
источник
Похоже, вы могли бы воспользоваться тем фактом, что для непрерывной работы в течение n дней потребуется n строк.
Так что-то вроде:
источник
Мне кажется слишком сложным сделать это с помощью одного SQL-запроса. Позвольте мне разбить этот ответ на две части.
ежедневное задание cron, которое проверяет каждого пользователя, вошедшего в систему сегодня, а затем увеличивает счетчик, если он есть, или устанавливает его на 0, если нет.
- Экспортируйте эту таблицу на сервер, на котором не работает ваш веб-сайт и который некоторое время не понадобится. ;)
- Сортировать по пользователю, затем по дате.
- пройти по порядку, держать счетчик ...
источник
Если это так важно для вас, создайте это событие и заведите стол, чтобы дать вам эту информацию. Не нужно убивать машину всеми этими безумными запросами.
источник
Вы можете использовать рекурсивный CTE (SQL Server 2005+):
источник
У Джо Селко есть полная глава об этом в SQL for Smarties (называющая это Runs and Sequences). У меня дома нет этой книги, поэтому, когда я приду на работу ... Я отвечу на это. (предполагается, что таблица истории называется dbo.UserHistory, а количество дней - @Days)
Еще одно замечание - из блога команды SQL о пробегах.
Другая идея, которая у меня была, но у меня нет удобного SQL-сервера для работы, - это использовать CTE с секционированным ROW_NUMBER следующим образом:
Вышеупомянутое, вероятно, НАМНОГО ТРУДЧЕ, чем должно быть, но оставлено как щекотка для мозга, когда у вас есть другое определение «пробежки», кроме свиданий.
источник
Пара вариантов SQL Server 2012 (при условии, что N = 100 ниже).
Хотя с моими образцами данных более эффективным оказалось следующее:
Оба полагаются на ограничение, указанное в вопросе, что существует не более одной записи в день на пользователя.
источник
Что-то вроде этого?
источник
Я использовал простое математическое свойство, чтобы определить, кто последовательно заходил на сайт. Это свойство заключается в том, что разница в днях между первым и последним доступом должна быть равна количеству записей в журнале таблицы доступа.
Вот SQL-скрипт, который я тестировал в Oracle DB (он должен работать и в других БД):
Скрипт подготовки стола:
источник
Оператор
cast(convert(char(11), @startdate, 113) as datetime)
удаляет временную часть даты, поэтому мы начинаем в полночь.Я хотел бы также предположить , что
creationdate
иuserid
столбцы индексируются.Я просто понял, что это не скажет вам всех пользователей и их общее количество последовательных дней. Но сообщит вам, какие пользователи будут посещать установленное количество дней с даты, которую вы выбрали.
Исправленное решение:
Я проверил это, и он будет запрашивать всех пользователей и все даты. Он основан на первом (шутливом?) Решении Спенсера , но мое работает.
Обновление: улучшена обработка даты во втором решении.
источник
Это должно делать то, что вы хотите, но у меня недостаточно данных для проверки эффективности. Запутанный материал CONVERT / FLOOR состоит в том, чтобы убрать временную часть из поля datetime. Если вы используете SQL Server 2008, вы можете использовать CAST (x.CreationDate AS DATE).
Скрипт создания
источник
Спенсер почти сделал это, но это должен быть рабочий код:
источник
Сверху моей головы MySQLish:
Непроверено и почти наверняка нуждается в некотором преобразовании для MSSQL, но я думаю, что это дает некоторые идеи.
источник
Как насчет того, чтобы использовать таблицы Tally? Он следует более алгоритмическому подходу, и план выполнения очень простой. Заполните tallyTable числами от 1 до «MaxDaysBehind», которые вы хотите сканировать в таблице (например, 90 будет искать на 3 месяца позже, и т. Д.).
источник
Немного подправили запрос Билла. Возможно, вам придется обрезать дату перед группировкой, чтобы считать только один вход в день ...
ИЗМЕНЕНО, чтобы использовать DATEADD (dd, DATEDIFF (dd, 0, CreationDate), 0) вместо convert (char (10), CreationDate, 101).
@IDisposable Раньше я искал использовать datepart, но мне было лень искать синтаксис, поэтому я решил, что вместо него id использует convert. Я не знаю, что это оказало значительное влияние. Спасибо! теперь я знаю.
источник
предполагая, что схема выглядит примерно так:
это позволит извлечь непрерывные диапазоны из последовательности дат с пробелами.
источник