Относительно использования вычисляемых столбцов, упомянутых в ответе, я хотел подтвердить утверждения о том, что использование вычисляемого столбца приведет к повышению производительности (для меня это не имело смысла, но я не гуру SQL Server). Результаты, которые я получил, показали, что использование вычисляемого столбца действительно медленнее - намного медленнее, чем простая группировка или подзапрос. Я провел тест на экземпляре SQL Server, установленном на моем собственном ПК - вот методология и результаты:
CREATE TABLE smb_header (keycol INTEGER NOT NULL
, name1 VARCHAR2(255)
, name2 VARCHAR2(255));
INSERT INTO smb_header
VALUES (1
, 'This is column 1'
, 'This is column 2'
);
INSERT INTO smb_header
SELECT (SELECT MAX(keycol)
FROM smb_header
) + keycol
, name1
, name2
FROM smb_header;
REM (repeat 20 times to generate ~1 million rows)
ALTER TABLE smb_header ADD PRIMARY KEY (keycol);
CREATE TABLE smb_detail (keycol INTEGER
, commentno INTEGER
, commenttext VARCHAR2(255));
INSERT INTO smb_detail
SELECT keycol
, 1
, 'A comment that describes this issue'
FROM smb_header;
ALTER TABLE smb_detail ADD PRIMARY KEY (keycol, commentno);
ALTER TABLE smb_detail ADD FOREIGN KEY (keycol)
REFERENCES smb_header (keycol);
INSERT INTO smb_detail
SELECT keycol
, (SELECT MAX(commentno)
FROM smb_detail sd2
WHERE sd2.keycol = sd1.keycol
) + commentno
, 'A comment that follows comment number '
+ CAST(sd1.commentno AS VARCHAR(32))
FROM smb_detail sd1
WHERE MOD(keycol, 31) = 0;
REM repeat 5 times, to create some records that have 64 comments
REM where others have one.
На данный момент в заголовке будет около 1 миллиона строк и по 1 или 64 комментария для каждого.
Теперь я создаю функцию (такую же, как ваша выше, только с именами моих столбцов и таблиц) и вычисляемый столбец:
alter table dbo.smb_header add CommentCountPersist as dbo.CountComments(keycol)
Кстати, PERSISTED не будет работать для этого столбца, как я и подозревал в моих комментариях выше - для SQL Server невозможно или слишком сложно отслеживать, какие строки нужно обновить, если вы ссылаетесь на другие таблицы в своей функции , Использование ключевого слова PERSISTED приводит к ошибке:
Msg 4934, Level 16, State 3, Line 1
Computed column 'CommentCountPersist' in table 'smb_header' cannot be
persisted because the column does user or system data access.
Это имеет смысл для меня - я не понимаю, как SQL Server мог определить, какие строки необходимо обновить при изменении других строк, для любой функции, которая может быть реализована, без ужасно неэффективного процесса обновления.
Теперь для тестов. Я создаю временную таблицу #holder для вставки строк - я хочу убедиться, что при выполнении моих запросов я обрабатываю весь набор результатов, а не только первые несколько строк, которые появятся в сеточном элементе управления Mgmt Studio.
SELECT h.keycol
, h.name1
, CommentCount
INTO #holder
FROM smb_header h
WHERE h.keycol < 0
Вот результаты моих запросов. Сначала вычисляемый столбец:
INSERT
INTO #holder
SELECT h.keycol
, h.name1
, CommentCount
FROM smb_header h
WHERE h.keycol between 5000 and 10000
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'Worktable'. Scan count 1, logical reads 10160, physical reads 0,
read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.
Table 'smb_header'. Scan count 1, logical reads 44, physical reads 0,
read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 265 ms, elapsed time = 458 ms.
(5001 row(s) affected)
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Теперь версия GROUP BY, вычисляемый столбец:
INSERT
INTO #holder
SELECT h.keycol
, h.name1
, COUNT(*)
FROM smb_header h
, smb_detail d
WHERE h.keycol between 5000 and 10000
AND h.keycol = d.keycol
GROUP BY h.keycol, h.name1
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'smb_header'. Scan count 1, logical reads 44, physical reads 0,
read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.
Table 'smb_detail'. Scan count 1, logical reads 366, physical reads 0,
read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 15 ms, elapsed time = 13 ms.
(5001 row(s) affected)
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Запись запроса с подзапросом в предложении SELECT, как Remus делал выше, дает тот же план и производительность, что и GROUP BY (что и следовало ожидать).
Как видите, вычисляемый столбец работает с версией значительно хуже. Это имеет смысл для меня, так как оптимизатор вынужден вызывать функцию и выполнять подсчет (*) для каждой строки в заголовке вместо использования более сложных методов разрешения двух наборов данных.
Возможно, я здесь что-то не так делаю. Мне было бы интересно, чтобы marc_s внес свой вклад в его выводы.