Как я могу обнаружить и компенсировать возможные недостатки в методе хранения дат?

#sql #sql-server #date #datetime

#sql #sql-сервер #Дата #datetime

Вопрос:

У меня есть список элементов, состояния которых находятся в пределах диапазонов дат, и некоторые подсчеты неверны (технически корректны, но неверны по замыслу).

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

Таблица выглядит примерно так:

 table_above
itemid | start                   | stop                    | state
1234   | 2000-01-01 00:00:00.000 | 2014-02-01 10:04:00.000 | 1
1234   | 2014-02-01 10:04:00.003 | NULL                    | 2
1111   | 2000-01-01 00:00:00.000 | NULL                    | 2
 

itemid 1234 был в state 1 течение примерно 14 лет, затем на Feb 1 него переключились state 2 , и stop = null средства, которые на данный момент, item 1234 есть state 2 .

item 1111 state 2 с тех пор 2000-01-01 00:00:00.000 и в state 2 настоящее время.

Был небольшой промежуток времени, когда БД вообще не регистрировала состояние itemid 1234 для следующих двух дат: '2014-02-01 10:04:00.001' и '2014-02-01 10:04:00.002'

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

Выполняемый мной запрос подсчитывает количество элементов в определенном состоянии в определенный момент времени:

 select
  count(1)
where
  start < '2014-02-01 12:00:00.000'
  and (stop >= '2014-02-01 12:00:00.000' or stop is null)
  and state = 2
from
  table_above
 

Это вернет 2 для table_above . Однако следующий запрос вернет 1 .

 select
  count(1)
where
  start < '2014-02-01 10:04:00.001'
  and (stop >= '2014-02-01 10:04:00.001' or stop is null)
  and state = 2
from
  table_above
 

Проблема в том, что на самом деле в течение этого крошечного периода времени элементы, связанные с этими itemid s, существуют, но они нигде не учитываются.

Запрос технически корректен, но мне нужно, чтобы он представлял намерение.

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

Кроме того, есть ли какой-либо способ выполнить запрос для печати всех пробелов для конкретного itemid ?

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

 table_above
itemid | start                   | stop                    | state
1234   | 2000-01-01 00:00:00.000 | 2014-02-01 10:04:00.000 | 1
1234   | 2014-02-01 10:03:59.999 | NULL                    | 2
 

Если бы я выполнил запрос выше дважды, с state = 2 или state = 1 для момента '2014-02-01 10:04:00.000' времени, оба возвращали бы количество 1 , но предполагается, что каждый элемент имеет только одно состояние в каждый момент времени, поэтому они не должны оба возвращаться 1

Комментарии:

1. TLDR, но почему состояние 2 началось до того, как состояние 1 остановилось в последнем примере?

2. Последним примером была запись данных, в которой я не уверен, происходит ли это, но я хотел бы проверить, так ли это. Реальное событие элемента, имеющего два состояния, не должно происходить, но я не знаю, происходит ли событие DB, подобное показанному (в конце).

3. вы не опубликовали никаких insert update инструкций nor. если данные появляются в базе данных с помощью черной магии (box), тогда возможно все.

4. @user38858: Дело не в том, кто отвечает за структуру. Речь идет о том, чтобы нам было проще давать вам ответы. Если вы не публикуете инструкции CREATE TABLE и INSERT , все, кто хочет ответить, должны либо перепроектировать ваш запрос, либо написать непроверенный код с головы до ног.

5. Концептуально, для информации о состоянии stop это производная информация, поскольку такой информации на самом деле не существует: на самом деле, это когда начинается следующее состояние. Тот факт, что существует два «непокрытых» значения, является исключительно результатом выбранного типа данных — если бы у вас было одно из других, их было бы гораздо больше. Какая версия SQL Server? В идеале вы могли бы использовать LAG() / LEAD() для создания «истинной» stop временной метки.

Ответ №1:

В другом ответе есть некоторая полезная информация, но вот еще кое-что, что также следует учитывать:

  • Если вы используете datetime тип, то значения всегда будут округляться с шагом .000, .003 или .007, потому что это точность. Это в документации.
  • Сравнение a datetime со строкой приведет к преобразованию этой строки в a datetime , поэтому на самом деле нет «крошечного периода» непредставления. Рассмотрим следующее:
     declare @dt0 datetime, @dt1 datetime, @dt2 datetime, @dt3 datetime
    select @dt0 = '2014-02-01 10:04:00.000',
           @dt1 = '2014-02-01 10:04:00.001',
           @dt2 = '2014-02-01 10:04:00.002',
           @dt3 = '2014-02-01 10:04:00.003'
    
    if @dt0 = @dt1 print 'True' else print 'False'
    if @dt2 = @dt3 print 'True' else print 'False'
     

    Оба теста печатаются True из-за округления datetime , происходящего с. Если вам действительно нужны более точные значения, рассмотрите возможность использования datetime2 вместо этого.

  • Ваша логика немного не подходит для сравнения диапазона. Обычно начало диапазона является включающим, а конец диапазона — исключительным. У вас все наоборот.

    Вместо:

     start < '2014-02-01 12:00:00.000'
    and (stop >= '2014-02-01 12:00:00.000' or stop is null)
     

    Это должно быть:

     start <= '2014-02-01 12:00:00.000'
    and (stop > '2014-02-01 12:00:00.000' or stop is null)
     

Комментарии:

1. Нет, какая сторона диапазона была включена, была правильной, он делает диапазон исключений. То есть у него на самом деле здесь два диапазона (один поверх start , другой поверх stop ), а другой привязан к значению max / min. Если какая-либо пара строк имеет совпадающие start / stop значения, ваша версия будет включать оба.

2. @Clockwork-Muse — Нет, мой также исключает перекрывающееся значение. Дело в том, что обычно начальное значение находится в пределах диапазона, в то время как конечное значение относится к следующему диапазону. Например, для диапазонов «от 1:00 до 2:00», «от 2:00 до 3:00» — значение 1:00 находится в первом диапазоне, в то время как 2:00 находится только во втором диапазоне, а 3:00 не входит ни в один из них.

Ответ №2:

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

Вы могли бы попробовать что-то вроде следующего, чтобы зафиксировать, когда дата начала / окончания состояния находится между датой начала и датой окончания другого состояния:

 Select A.itemid
From table_above A
    Join table_above B
    On A.itemid = B.itemid
Where B.start Between A.start And A.stop
       Or B.stop Between A.start And A.stop
 

Комментарии:

1. Что, если B.start не был между A.start и A.stop, но B.stop был ?

2. @MikeSherrilCatRecall Отредактирован для приведенного выше сценария.

Ответ №3:

Кроме того, есть ли какой-либо способ выполнить запрос для печати всех пробелов для определенного itemid?

В SQL Server 2012 вы можете сделать это довольно легко с помощью аналитической функции lag(). (В более ранних версиях используйте подзапросы.)

 select *
from (select itemid, start, stop, state, 
             lag(stop) over (partition by itemid order by start) prev_stop
      from table_above) x
where start <> prev_stop;
 

Этот запрос выдаст вам все строки, в которых значение для «start» отличается от значения для предыдущего «stop». Вы можете добавить «itemid» в предложение WHERE, если вам нужно проверить наличие одного itemid, и вы можете вычесть «prev_stop» из «start», если хотите считать значения в пределах, скажем, 500 мсек равными.

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

Я думаю, это почти то же самое, что сказать: «Когда есть пробел, измените время остановки на равное следующему времени начала». Если я прав в этом, то я думаю, что этот запрос дает вам хорошую рабочую таблицу.

 select itemid,
       start,
       case when datediff(ms, next_start, stop) <= 500 then next_start
            else stop
       end as stop,
       state
from
  (select *, lead(start) over (partition by itemid order by start) next_start
  from table_above) x;
 

Я бы, вероятно, заключил это в инструкцию CREATE VIEW и основывал свои подсчеты на нем, а не на базовых таблицах.