Когда инициализируется значение по умолчанию?

#sql-server #sql

#sql-server #sql

Вопрос:

 create table tab3(a integer,d1 datetime default getdate())
insert into tab3(a) values(1)
insert into tab3 (a) select a from tab3
GO 20
insert into tab3 (a) select a from tab3

select d1,count(*) from tab3(NOLOCK) group by d1
 

Окончательная вставка определенно занимает нетривиальное количество времени (3 секунды на моей машине)

Однако значение в d1 отличается для каждого пакета. т.Е. Конечный запрос возвращает только 22 строки

 2014-06-22 20:34:53.787 1
2014-06-22 20:34:56.127 1
2014-06-22 20:34:56.140 2
2014-06-22 20:34:56.153 4
2014-06-22 20:34:56.157 8
2014-06-22 20:34:56.160 16
2014-06-22 20:34:56.163 32
2014-06-22 20:34:56.167 64
2014-06-22 20:34:56.170 128
2014-06-22 20:34:56.177 256
2014-06-22 20:34:56.183 512
2014-06-22 20:34:56.193 1024
2014-06-22 20:34:56.210 2048
2014-06-22 20:34:56.240 4096
2014-06-22 20:34:56.293 8192
2014-06-22 20:34:56.397 16384
2014-06-22 20:34:56.493 32768
2014-06-22 20:34:56.607 65536
2014-06-22 20:34:56.817 131072
2014-06-22 20:34:57.240 262144
2014-06-22 20:34:57.710 524288
2014-06-22 20:35:01.630 1048576
 

Почему GETDATE() инициализируется для каждого оператора, а не для каждой вставки?

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

РЕДАКТИРОВАТЬ: вероятно, связано, tab3_log имеет ту же схему, что и tab3

 CREATE TRIGGER tab3_logger on tab3
AFTER INSERT
AS
BEGIN
INSERT INTO tab3_log(a) select a from inserted
END
 

Показано, что все строки из одного оператора insert имеют одинаковую дату и время

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

1. Это особенность SQL Server / проблема проектирования. Но зачем вам это нужно?

2. @usr У меня в таблице выполняется ведение журнала на основе триггера, при этом в таблице журнала используется GETDATE() по умолчанию. Заметил это поведение оттуда и создал некоторые тестовые данные для исследования. Теперь мне любопытно, почему это происходит

Ответ №1:

Когда инициализируется значение по умолчанию?

Это зависит от того, является ли выражение «константой времени выполнения«.

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

 CREATE FUNCTION dbo.F()
RETURNS DATETIME
AS
  BEGIN
      RETURN GETDATE()
  END

GO

CREATE TABLE T
  (
     A CHAR(8000) NULL,
     B FLOAT DEFAULT RAND(), 
     C DATETIME DEFAULT GETDATE(),
     D DATETIME DEFAULT dbo.F(),
     E UNIQUEIDENTIFIER DEFAULT NEWID()
  )

INSERT INTO T
            (A)
SELECT TOP 100000 'A'
FROM   master..spt_values v1,
       master..spt_values v2

SELECT COUNT(DISTINCT B) AS B,
       COUNT(DISTINCT C) AS C,
       COUNT(DISTINCT D) AS D,
       COUNT(DISTINCT E) AS E
FROM   T

GO

DROP TABLE T;
DROP FUNCTION F;
 

Возвращает (пример, точное значение для D будет отличаться)

  --- --- ----- -------- 
| B | C |  D  |   E    |
 --- --- ----- -------- 
| 1 | 1 | 823 | 100000 |
 --- --- ----- -------- 
 

Первые два вычисляются для каждого оператора, а вторые два для каждой строки.

Ответ №2:

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

Ответ №3:

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

Не уверен, как решить, но я бы попробовал это:

 insert into tab3 (a, d1) select a, getdate() from tab3
GO 20
 

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

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

2. Затем вам придется попробовать использовать некоторую хранимую процедуру с циклом и вставкой в него. Я думаю, было бы справедливо ожидать, что getdate() будет оцениваться для каждой строки в select . Но раньше Microsoft была такой.