#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 была такой.