web-dev-qa-db-ja.com

動的SQLによる列名のループ

私は、各列のすべての個別の値を表示し、それぞれのレコード数を数えるコードのアイデアを思いついたところです。コードにすべての列をループさせたい。

これが私がこれまでに持ってきたものです...私はSQLに慣れていないので、初心者です:)

ハードコード:

  select [Sales Manager], count(*)
  from  [BT].[dbo].[test]
  group by [Sales Manager]
  order by 2 desc

動的SQLを試す:

Declare @sql varchar(max),
@column as varchar(255)

    set @column = '[Sales Manager]'
    set @sql = 'select ' + @column + ',count(*) from [BT].[dbo].[test] group by ' + @column + 'order by 2 desc'

    exec (@sql)

これらはどちらも正常に動作します。すべての列をループさせるにはどうすればよいですか?列名をハードコーディングする必要があるかどうかは問題ではありません。@ columnの各列にサブビューを追加することで、その方法で機能します。

これは理にかなっていますか?

皆さんありがとう!

7
Lucas

動的SQLを使用して、テーブルのすべての列名を取得できます。次に、スクリプトを作成します。

Declare @sql varchar(max) = ''
declare @tablename as varchar(255) = 'test'

select @sql = @sql + 'select [' + c.name + '],count(*) as ''' + c.name +  ''' from [' + t.name + '] group by [' + c.name + '] order by 2 desc; ' 
from sys.columns c
inner join sys.tables t on c.object_id = t.object_id
where t.name = @tablename

EXEC (@sql)

変化する @tablenameをテーブルの名前(データベースまたはスキーマ名なし)に変更します。

7
Szymon

これは少しXYの回答ですが、列名をハードコーディングしてもかまわない場合は、それだけを行い、動的SQL(およびループ)を完全に回避することをお勧めします。ダイナミックSQLは一般に最後の手段と考えられており、慎重に行わないとセキュリティの問題(SQLインジェクション攻撃)が発生しやすく、クエリや実行プランをキャッシュできない場合は遅くなることがよくあります。

大量の列名がある場合は、簡単なコードを作成するか、Wordで差し込み印刷を行って、代わりに置き換えることができます。


ただし、列名を取得する方法に関しては、これがSQL Serverであると想定すると、次のクエリを使用できます。

_SELECT c.name
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.test')
_

したがって、このクエリから動的SQLを構築できます。

_SELECT 'select ' 
    + QUOTENAME(c.name) 
    + ',count(*) from [BT].[dbo].[test] group by ' 
    + QUOTENAME(c.name)  
    + 'order by 2 desc'
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.test')
_

カーソルを使用してループします。

または、全体を1つのバッチにまとめて実行します。ここでは、FOR XML PATH('')トリックを使用します。

_DECLARE @sql VARCHAR(MAX) = (
    SELECT ' select ' --note the extra space at the beginning
        + QUOTENAME(c.name) 
        + ',count(*) from [BT].[dbo].[test] group by ' 
        + QUOTENAME(c.name)  
        + 'order by 2 desc'
    FROM sys.columns c
    WHERE c.object_id = OBJECT_ID('dbo.test')
    FOR XML PATH('')
)

EXEC(@sql)
_

組み込みQUOTENAME関数 を使用して、エスケープが必要な列名をエスケープしています。

8
lc.