WedX - журнал о программировании и компьютерных науках

SQL Server МЕЖДУ

У меня есть таблица с годом, месяцем и несколькими числовыми столбцами.

Year   Month  Total
2011     10    100
2011     11    150
2011     12    100  
2012     01    50
2012     02    200

Теперь я хочу SELECT строк в период с ноября 2011 г. по февраль 2012 г. Обратите внимание, что я хочу, чтобы запрос использовал диапазон. Так же, как если бы у меня был столбец даты в таблице.


  • Если вы хотите использовать диапазон, вам нужно выбрать вычисляемый столбец (например, concat (год, месяц)), и это будет плохо для производительности. Лучше использовать (по общему признанию, довольно сложный) запрос для двух столбцов по отдельности, потому что тогда он может использовать индексы. 20.12.2012

Ответы:


1

Придумать способ использования BETWEEN с таблицей как она есть, будет работать, но в любом случае будет хуже с точки зрения производительности:

  • В лучшем случае будет потребляться больше ЦП, чтобы выполнять какие-то вычисления в строках, а не работать с ними как с датами.
  • В худшем случае это вызовет сканирование таблицы для каждой строки в таблице, но если ваши столбцы имеют индексы, то при правильном запросе возможен поиск. Это может быть ОГРОМНОЙ разницей в производительности, потому что принудительное включение ограничений в предложение BETWEEN отключит использование индекса.

Вместо этого я предлагаю следующее, если у вас есть индекс в столбцах даты и вы вообще заботитесь о производительности:

DECLARE
   @FromDate date = '20111101',
   @ToDate date = '20120201';

SELECT *
FROM dbo.YourTable T
WHERE
   (
      T.[Year] > Year(@FromDate)
      OR (    
         T.[Year] = Year(@FromDate)
         AND T.[Month] >= Month(@FromDate)
      )
   ) AND (
      T.[Year] < Year(@ToDate)
      OR (
         T.[Year] = Year(@ToDate)
         AND T.[Month] <= Month(@ToDate)
      )
   );

Однако понятно, что вы не хотите использовать такую ​​конструкцию, так как она очень неудобна. Итак, вот компромиссный запрос, который, по крайней мере, использует числовые вычисления и будет использовать меньше ЦП, чем вычисление преобразования даты в строку (хотя и недостаточно меньше, чтобы компенсировать принудительное сканирование, которое является реальной проблемой производительности).

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;

Если у вас есть индекс Year, вы можете получить большой прирост, отправив запрос следующим образом, который имеет возможность поиска:

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
   AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]

Хотя это нарушает ваше требование использовать одно выражение BETWEEN, это не слишком болезненно и будет очень хорошо работать с индексом Year.

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

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

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

ALTER Table dbo.YourTable
   ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
   PERSISTED;

С этим вы можете просто сделать:

SELECT *
FROM dbo.YourTable
WHERE
   ActualDate BETWEEN '20111101' AND '20120201';

Ключевое слово PERSISTED означает, что, хотя вы все равно получите сканирование, ему не нужно будет выполнять какие-либо вычисления для каждой строки, поскольку выражение вычисляется при каждой операции INSERT или UPDATE и сохраняется в строке. Но вы можете получить поиск, если добавите индекс к этому столбцу, что повысит его производительность (хотя в целом это все же не так идеально, как переход на использование фактического столбца дат, потому что это займет больше места и повлияет на INSERT и UPDATE):

CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);

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

20.12.2012

2

Похоже, что в вашей примерной таблице указано, что есть только одна запись в год и месяц (если это действительно сводная таблица по месяцам). Если это так, вы, вероятно, накопите очень мало данных в таблице даже за несколько десятилетий деятельности. Решение с объединенным выражением будет работать, и производительность (в данном случае) не будет проблемой:

SELECT * FROM Table WHERE ((Year * 100) + Month) BETWEEN 201111 AND 201202

Если это не так и у вас действительно большое количество записей в таблице (более нескольких тысяч записей), у вас есть несколько вариантов:

  1. Измените свою таблицу, чтобы хранить год и месяц в формате ГГГГММ (либо в виде целочисленного значения, либо в виде текста). Этот столбец может заменить ваш текущий год и индексный столбец или быть дополнением к ним (хотя это нарушает нормальную форму). Проиндексируйте этот столбец и запросите его.

  2. Создайте отдельную таблицу с одной записью в год и месяц, а также индексируемый столбец, как описано выше. В своем запросе ПРИСОЕДИНИТЕ эту таблицу к исходной таблице и выполните запрос к индексированному столбцу в меньшей таблице.

20.12.2012
  • Я не думаю, что № 2 имеет смысл - зачем создавать отдельную таблицу!?!? # 1 и предоставленный вами запрос являются дубликатами информации, уже предоставленной в других ответах. 20.12.2012
  • Создание отдельной таблицы позволяет хранить индексируемые значения без необходимости их повторения в десятках тысяч строк в таблице данных (если такое количество строк существует). Это значительно снижает вычислительную нагрузку при поддержании значений индекса, нормализует взаимосвязь между значениями индекса и парами год-месяц, которые они отображают, и может быть реализовано вообще без каких-либо разрешений для исходной таблицы. Однако он вводит дополнительный JOIN, поэтому я сначала упомянул менее обычную форму. 20.12.2012
  • Я понимаю, что ты имеешь в виду, Ларри. Теперь имеет смысл подчеркивать отношение «один ко многим». 20.12.2012

  • 3
  • Я хотел бы отметить, что это функционально идентично первому запросу в моем ответе. У вас есть 6 условий и 5 соединений. Мой первый заданный запрос имеет точно такие же 6 условий и 5 конъюнкций, просто немного переставленных. 20.12.2012
  • И версия Эрика более разборчива ИМХО. В частности, вам не нужно знать, что связывает И или ИЛИ сильнее. 20.12.2012
  • @ErikE Приношу свои извинения, я должен был быть более осторожным, публикуя свой ответ. Я вижу, что в вашем ответе обычно используется тот же подход, помимо некоторых других хороших предложений. Однако рассмотрите возможность пересмотра своего запроса, так как он не работает в некоторых случаях, когда @FromDate и @ToDate относятся к одному и тому же году. Если вы решите эту проблему, я удалю свой ответ, так как он не содержит много информации. Подобная настройка таблицы (год и месяц в виде отдельных столбцов) в прошлом не раз подводила меня. 21.12.2012
  • Хорошо поймал! Вы абсолютно правы насчет моих условий. Я скоро это исправлю. 21.12.2012
  • Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

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


    Для любых предложений по сайту: [email protected]