带有/ Loop Over Table in Table中的动态SQL

时间:2014-08-28 22:37:10

标签: sql sql-server-2008

我最近在我的新工作中脱离了一个ASP.net转换项目,以帮助解决另一个部门正在处理的相当缓慢,平凡但绝望的任务。基本上,他们在每个数据库中每个表的每一列上都使用一个简单的SQL脚本(这很糟糕),以便为每列生成每个表上所有不同记录的计数。我的SQL体验是有限的,我的动态SQL体验是零,或多或少,但由于我还没有获得权限甚至访问这个特定的数据库我开始尝试制定一个更自动化的查询来执行此任务,测试我有权访问的数据库。

简而言之,我遇到了一些问题,我希望有人可以帮助我填补空白。如果可以使用更自动化的东西,它将为这个部门节省超过一个月的推测时间。

这是我给出的两个脚本,并告诉我们在每一列上运行。第一个用于任何非位/布尔列,也用于非日期时间列。第二个用于任何日期时间列。

select columnName, count(*) qty
from tableName
group by columnName
order by qty desc

select year(a.columnName), count(*) qty
from tableName a
group by year(a.columnName)
order by qty desc

这样做几千次对我来说似乎不是很有趣,所以这里或多或少是我提出的一些伪代码,我认为可以解决问题,我会指出哪些方面我我很模糊。

declare @sql nvarchar(2500)

set @sql = 'the first part(s) of statement'

[pseudo-pseudo]获取表中所有列名的“列表”(我不相信SQL代码中有一个Collection数据类型,但你明白了)

[pseudo-pseudo]循环通过列名称的“列表”

(我知道这个点符号不起作用,但我想执行与此类似的操作)

IF ColumnName.DataType LIKE 'date%'
set @sql = @sql + ' something'

IF ColumnName.DataType = bit
set @sql = @sql + ' something else' --actually it'd be preferable to skip bit/boolean datatypes      if possible as these aren't necessary for the reports being created by these queries

ELSE
set @sql = @sql + ' something other than something else'

set @sql = @sql + ' ending part of statement'

EXEC(@sql)

总而言之,为了简单起见,我想让用户在查询开始时将表的名称插入变量中:

declare @tableName nvarchar(50)
set @tableName = 'TABLENAME' --Enter Query's Table Name Here

基于此,代码将循环遍历该表的每一列,检查数据类型。如果数据类型是日期时间(或其他日期,如数据类型),则“年”代码将添加到动态SQL中。如果它是其他任何东西(除了bit / boolean),那么它会将默认逻辑添加到动态SQL代码中。

同样,为了简单起见(即使这是不好的做法),我认为最终结果将是一个带有多个选择的动态SQL语句,表中的每一列都有一个。然后用户只需将输出复制到excel(无论如何他们现在都在做)。我知道这不是一个完美的解决方案所以我愿意接受建议,但由于时间紧迫,而且我对动态SQL的经验接近于零,我认为在这种情况下,一种快速而肮脏的方法是可以接受的。 / p>

我为这个问题做了非常随意的准备而道歉,但我确实希望那里的某个人能够引导我朝着正确的方向前进。

非常感谢您的时间,我当然感激不尽。

1 个答案:

答案 0 :(得分:1)

以下是处理评论中所有建议的示例。

declare @sql nvarchar(max);
declare stat_cursor cursor local fast_forward for
select
    case when x.name not in ('date', 'datetime2', 'smalldatetime', 'datetime') then

N'select
    ' + quotename(s.name, '''') + ' as schema_name,
    ' + quotename(t.name, '''') + ' as table_name,
    ' + quotename(c.name) + ' as column_name,
    count(*) qty
from
    ' + quotename(s.name) + '.' + quotename(t.name) + '
group by 
    ' + quotename(c.name) + '
order by 
    qty desc;'

    else

N'select
    ' + quotename(s.name, '''') + ' as schema_name,
    ' + quotename(t.name, '''') + ' as table_name,
    year(' + quotename(c.name) + ') as column_name,
    count(*) qty
from
    ' + quotename(s.name) + '.' + quotename(t.name) + '
group by 
    year(' + quotename(c.name) + ')
order by 
    qty desc;'

    end

from
    sys.schemas s
        inner join
    sys.tables t
        on s.schema_id = t.schema_id
        inner join
    sys.columns c
        on c.object_id = t.object_id
        inner join
    sys.types x
        on c.system_type_id = x.user_type_id
where
    x.name not in (
        'geometry',
        'geography',
        'hierarchyid',
        'xml',
        'timestamp',
        'bit',
        'image',
        'text',
        'ntext'
    );

open stat_cursor;

fetch next from stat_cursor into @sql;

while @@fetch_status = 0
begin
    exec sp_executesql @sql;
    fetch next from stat_cursor into @sql;
end;

close stat_cursor;
deallocate stat_cursor;

Example SQLFiddle(注意这只显示了游标的第一次迭代。不确定这是否是SQLFiddle的限制或错误)。

如果我这样做,我可能会将结果存入一个单独的数据库。此外,我可能会将SQL构建位置于用户定义的函数中以实现可维护性(缓慢的位将运行查询,没有点优化生成它们)。