Для работы мне нужно было подсчитать количество строк во всех таблицах схемы. Писать селект для каждой таблицы было лень, потому пришла мысль написать один скрипт, который можно было бы использовать на разных базах данных без модификации. Что из этого получилось.
Должен пояснить, что скрипт я пишу для Sql Server 2008 R2. Для запроса я решил использовать системное представление информационной схемы TABLES, хотя для нахождения схемы таблицы его и не рекомендуют, я пока не встретил ни одного случая, где схема, указанная в представлении, не соответствовала реальной схеме базы данных. Далее скрипт и скриншот.
В результате выполнения скрипта я получил то, чего ожидал.
Конечно, можно было сделать и через курсоры. Но этот вариант мне кажется проще.
UPD.
Переписал скрипт с учетом того, что представление TABLE не рекомендуют для получения названия схемы. Для этого использовал системную таблицу sys.objects (Transact-SQL) и функцию SCHEMA_NAME (Transact-SQL)
Как верно заметили в комментариях, можно также использовать недокументированные возможности и получить аналогичный результат
Код, конечно, стал меньше. Но недокументированные возможности ведут за собой недокументррованные ошибки.
Также есть ещё вариант получения количества строк в таблицах
Но в этом случае есть вероятность, что данные будут неактуальные.
UPD2. Если Вам достаточно получить примерное количество строк в таблице, то можно воспользоваться представлением sys.partitions (Transact-SQL)
Код получился намного короче, но в результате может быть погрешность.
UPD3.
В результате изучения документации, я всё таки нашел способ точного подсчёта количества строк в таблицах. Для этого следовало использовать представление sys.dm_db_partition_stats (Transact-SQL)
Этот способ нахождения количества строк в таблицах базы данных мне кажется самым лучшим из представленных.
Должен пояснить, что скрипт я пишу для Sql Server 2008 R2. Для запроса я решил использовать системное представление информационной схемы TABLES, хотя для нахождения схемы таблицы его и не рекомендуют, я пока не встретил ни одного случая, где схема, указанная в представлении, не соответствовала реальной схеме базы данных. Далее скрипт и скриншот.
- -- Код, получает количество строк во всех таблицах
- -- тут будет храниться список запросов к каждой таблице в базе
- DECLARE @sqls TABLE (rownumber int, sqls varchar(max)) ;
- -- это для прохода по таблице
- DECLARE @i bigint;
- DECLARE @numrows bigint;
- -- временная переменная
- DECLARE @temp varchar(max);
- -- тут будет результирующий запрос
- DECLARE @SQL varchar(max) = '';
- -- получаем запросы к каждой таблице в базе
- INSERT INTO @sqls (rownumber, sqls)
- SELECT
- -- номер строки в результате - нам понадобится при перечислении
- ROW_NUMBER()
- OVER (
- ORDER BY
- t.TABLE_CATALOG,
- t.TABLE_SCHEMA,
- t.TABLE_NAME
- ),
- -- Сам запрос в виде SELECT ('БазаДаных.Схема.Таблица') as TableName, (SELECT COUNT(*) FROM БазаДаных.Схема.Таблица) as cnt
- 'SELECT (''' +
- t.TABLE_CATALOG + '.' +
- t.TABLE_SCHEMA + '.' +
- t.TABLE_NAME
- + ''') as TableName, ' +
- '(SELECT COUNT(*) FROM ' +
- t.TABLE_CATALOG + '.' +
- t.TABLE_SCHEMA + '.' +
- t.TABLE_NAME +
- ') as cnt'
- -- все нужные нам данные находятся в таблице INFORMATION_SCHEMA.TABLES
- FROM INFORMATION_SCHEMA.TABLES t
- WHERE
- t.TABLE_TYPE = 'BASE TABLE' -- Указывает, что мы ищем по таблицам
- ORDER BY
- t.TABLE_CATALOG,
- t.TABLE_SCHEMA,
- t.TABLE_NAME
- -- Цикл. Тут нужно обойти все полученные записи и просто сцепить в один запрос, через UNION ALL
- -- i - просто счётчик от 1 до numrows
- SET @i = 1
- SET @numrows = (SELECT COUNT(*) FROM @sqls)
- IF @numrows > 0
- WHILE (@i <= (SELECT MAX(rownumber) FROM @sqls))
- BEGIN
- -- Получаем нужную строку
- SET @temp = (SELECT sqls FROM @sqls WHERE rownumber = @i)
- -- Склеиваем
- IF (@i = 1)
- BEGIN
- SET @SQL = @temp;
- END
- ELSE
- BEGIN
- SET @SQL = @SQL + ' UNION ALL '+ @temp;
- END
- -- Увеличиваем счётчик
- SET @i = @i + 1
- END
- -- Выполняем полученный запрос
- EXEC(@SQL)
Конечно, можно было сделать и через курсоры. Но этот вариант мне кажется проще.
UPD.
Переписал скрипт с учетом того, что представление TABLE не рекомендуют для получения названия схемы. Для этого использовал системную таблицу sys.objects (Transact-SQL) и функцию SCHEMA_NAME (Transact-SQL)
- -- Код, получает количество строк во всех таблицах
- -- тут будет храниться список запросов к каждой таблице в базе
- DECLARE @sqls TABLE (rownumber int, sqls varchar(max)) ;
- -- это для прохода по таблице
- DECLARE @i bigint;
- DECLARE @numrows bigint;
- -- временная переменная
- DECLARE @temp varchar(max);
- -- тут будет результирующий запрос
- DECLARE @SQL varchar(max) = '';
- -- получаем запросы к каждой таблице в базе
- INSERT INTO @sqls (rownumber, sqls)
- SELECT
- -- номер строки в результате - нам понадобится при перечислении
- ROW_NUMBER()
- OVER (
- ORDER BY
- t.TABLE_CATALOG,
- SCHEMA_NAME(ob.schema_id),
- t.TABLE_NAME
- ),
- -- Сам запрос в виде SELECT ('БазаДаных.Схема.Таблица') as TableName, (SELECT COUNT(*) FROM БазаДаных.Схема.Таблица) as cnt
- 'SELECT (''' +
- t.TABLE_CATALOG + '.' +
- SCHEMA_NAME(ob.schema_id) + '.' +
- t.TABLE_NAME
- + ''') as TableName, ' +
- '(SELECT COUNT(*) FROM ' +
- t.TABLE_CATALOG + '.' +
- SCHEMA_NAME(ob.schema_id) + '.' +
- t.TABLE_NAME +
- ') as cnt'
- -- все нужные нам данные находятся в таблице INFORMATION_SCHEMA.TABLES
- FROM
- INFORMATION_SCHEMA.TABLES t INNER JOIN
- sys.objects ob ON ob.[name] = t.TABLE_NAME
- WHERE
- t.TABLE_TYPE = 'BASE TABLE' -- Указывает, что мы ищем по таблицам
- ORDER BY
- t.TABLE_CATALOG,
- SCHEMA_NAME(ob.schema_id),
- t.TABLE_NAME
- -- Цикл. Тут нужно обойти все полученные записи и просто сцепить в один запрос, через UNION ALL
- -- i - просто счётчик от 1 до numrows
- SET @i = 1
- SET @numrows = (SELECT COUNT(*) FROM @sqls)
- IF @numrows > 0
- WHILE (@i <= (SELECT MAX(rownumber) FROM @sqls))
- BEGIN
- -- Получаем нужную строку
- SET @temp = (SELECT sqls FROM @sqls WHERE rownumber = @i)
- -- Склеиваем
- IF (@i = 1)
- BEGIN
- SET @SQL = @temp;
- END
- ELSE
- BEGIN
- SET @SQL = @SQL + ' UNION ALL '+ @temp;
- END
- -- Увеличиваем счётчик
- SET @i = @i + 1
- END
- -- Выполняем полученный запрос
- EXEC(@SQL)
Как верно заметили в комментариях, можно также использовать недокументированные возможности и получить аналогичный результат
- CREATE TABLE #counts
- (
- table_name varchar(255),
- row_count int
- )
- EXEC sp_MSForEachTable @command1='INSERT #counts (table_name, row_count) SELECT ''?'', COUNT(*) FROM ?'
- SELECT table_name, row_count FROM #counts ORDER BY table_name, row_count DESC
- DROP TABLE #counts;
Код, конечно, стал меньше. Но недокументированные возможности ведут за собой недокументррованные ошибки.
Также есть ещё вариант получения количества строк в таблицах
- SELECT
- o.Name,
- i.rows
- FROM sysobjects o
- INNER JOIN sysindexes i
- ON (o.id = i.id)
- WHERE o.xtype = 'u'
- AND i.indid < 2
- ORDER BY o.name
Но в этом случае есть вероятность, что данные будут неактуальные.
UPD2. Если Вам достаточно получить примерное количество строк в таблице, то можно воспользоваться представлением sys.partitions (Transact-SQL)
- SELECT
- schema_name(o.schema_id) + '.' + o.Name,
- p.rows
- FROM sys.objects o
- INNER JOIN sys.partitions p
- ON (o.object_id = p.object_id)
- WHERE
- o.type = 'u'
- AND p.index_id < 2
- ORDER BY
- schema_name(o.schema_id) + '.' + o.Name
Код получился намного короче, но в результате может быть погрешность.
UPD3.
В результате изучения документации, я всё таки нашел способ точного подсчёта количества строк в таблицах. Для этого следовало использовать представление sys.dm_db_partition_stats (Transact-SQL)
- SELECT
- schema_name(o.schema_id) + '.' + o.Name,
- p.row_count
- FROM sys.objects o
- INNER JOIN sys.dm_db_partition_stats p
- ON (o.object_id = p.object_id)
- WHERE
- o.type = 'u'
- AND p.index_id < 2
- ORDER BY
- schema_name(o.schema_id) + '.' + o.Name
Этот способ нахождения количества строк в таблицах базы данных мне кажется самым лучшим из представленных.
Можно вот так сделать то же самое
ОтветитьУдалитьCREATE TABLE #counts
(
table_name varchar(255),
row_count int
)
EXEC sp_MSForEachTable @command1='INSERT #counts (table_name, row_count) SELECT ''?'', COUNT(*) FROM ?'
SELECT table_name, row_count FROM #counts ORDER BY table_name, row_count DESC
Но это недокументированная возможность.
Спасибо большое!
УдалитьЯ потому и не стал её использовать - так как это недокументрованная возможность. Хотелось чего то стопудового :)
ОтветитьУдалить"Но в этом случае есть вероятность, что данные будут неактуальные."
ОтветитьУдалитьС чего ты взял?
Потому что системная таблица sys.sysindexes (Transact-SQL) устарела и поддерживается только для обратной совместимости. Вместо неё MSDN советует использовать
ОтветитьУдалитьsys.indexes,
sys.partitions,
sys.allocation_units,
sys.dm_db_partition_stats. Я бы мог использовать поле rows представления sys.partitions, но оно хранит только приблизительное количество строк.
Спасибо большое за последний способ, реально очень удобно!
ОтветитьУдалить