2013-05-10 72 views
1

我有一個可以由同一個PK列連接在一起的表的列表。由於此列表可能因項目而異,因此我想創建一個查詢,該查詢可以足夠動態以從這些表中提取所有唯一列。從連接表列表中選擇唯一的列名稱

For example, I have three tables below: 
Table A (PK field, column1, column 2) 
Table B (PK field, column3, column 4) 
Table C (PK field, column5, column 5) 

這三個表的連接「PK場」一欄,我想查詢輸出是這樣的:

PK field column1 column2 column3 column4 column5 
..data.. ..data.. ..data.. ..data.. ..data.. ..data.. 

最後,這個查詢將是一個SQL函數的一部分或SP,因此用戶可以定義一個表的列表,並在開始時定義PK字段,然後執行它將使用數據集返回我的預期輸出。

我想使用低於此查詢,但結果不是我所喜歡的:

SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '' 

約我應該怎麼設計這個SP或功能的任何建議,將不勝感激。

在此先感謝。

DDL的兩個示例表:

CREATE TABLE [dbo].[G_bDEM](
    [blaiseKey_code] [nvarchar](255) NULL, 
    [qSex] [int] NULL, 
    [qDOB] [datetime] NULL, 
    [qDOBNR] [int] NULL, 
    [qAge] [int] NULL, 
    [qAgeNR] [int] NULL, 
    [qAgeRange] [int] NULL, 
    [qAge15OrOver] [int] NULL, 
    [qNotEligible] [nvarchar](1) NULL, 
    [qBornInNZ] [int] NULL, 
    [qCountryOfBirth] [nvarchar](2) NULL, 
    [qArriveNZYr] [int] NULL, 
    [qArriveNZYrNR] [int] NULL, 
    [qArriveNZMth] [int] NULL, 
    [bDEM_BOP_qHowManyRaised] [int] NULL, 
    [bDEM_BOP_q1stParentBornNZ] [int] NULL, 
    [bDEM_BOP_q2ndParentBornNZ] [int] NULL, 
    [bDEM_BOP_qHowManyParentBornNZ] [int] NULL, 
    [qMaoriDescent] [int] NULL, 
    [qSchQual] [int] NULL, 
    [qSchQualOth] [nvarchar](200) NULL, 
    [qSchQualOthNR] [int] NULL, 
    [qSchQualYr] [int] NULL, 
    [qSchQualYrNR] [int] NULL, 
    [qPostSchQual] [int] NULL, 
    [q3MthsStudy] [int] NULL, 
    [qHighestQual] [int] NULL, 
    [qHighestQualOth] [nvarchar](200) NULL, 
    [qHighestQualOthNR] [int] NULL, 
    [qHighestQualYr] [int] NULL, 
    [qHighestQualYrNR] [int] NULL, 
    [qWorkIntro] [nvarchar](1) NULL, 
    [qDidPaidWork] [int] NULL, 
    [qAwayFromWork] [int] NULL, 
    [qFamilyBusWork] [int] NULL, 
    [bDEM_WOR_qPaidWorkIntro] [nvarchar](1) NULL, 
    [bDEM_WOR_qJobsNum] [int] NULL, 
    [bDEM_WOR_qJobsNumNR] [int] NULL, 
    [bDEM_WOR_tabDEM_T2_fTotMins] [int] NULL, 
    [bDEM_WOR_q2JobsNoHrsIntro] [nvarchar](1) NULL, 
    [bDEM_WOR_q2Jobs2HrsIntro] [nvarchar](1) NULL, 
    [bDEM_WOR_q2Jobs1HrsIntro] [nvarchar](1) NULL, 
    [bDEM_WOR_qOccupation] [nvarchar](200) NULL, 
    [bDEM_WOR_qOccupationNR] [int] NULL, 
    [bDEM_WOR_qMainTasks] [nvarchar](200) NULL, 
    [bDEM_WOR_qMainTasksNR] [int] NULL, 
    [bDEM_WOR_qFeelAboutJob] [int] NULL, 
    [bDEM_WOR_qEmployArrangement] [int] NULL, 
    [bDEM_WOR_qPermEmployee] [int] NULL, 
    [qHasJobToStart] [int] NULL, 
    [qLookedForWork] [int] NULL, 
    [qJobSearchA] [int] NULL, 
    [qJobSearchB] [int] NULL, 
    [qJobSearchC] [int] NULL, 
    [qJobSearchD] [int] NULL, 
    [qJobSearchE] [int] NULL, 
    [qJobSearchF] [int] NULL, 
    [qJobSearchG] [int] NULL, 
    [qJobSearchH] [int] NULL, 
    [qJobSearchI] [int] NULL, 
    [qJobSearchOth] [nvarchar](200) NULL, 
     [qJobSearchOthNR] [int] NULL, 
    [qCouldStartLastWk] [int] NULL, 
    [qIncTotalAmt] [int] NULL, 
    [fCountryName] [nvarchar](60) NULL 
    ) ON [PRIMARY] 

    GO 

CREATE TABLE [dbo].[G_bLWW](
    [blaiseKey_code] [nvarchar](255) NULL, 
    [qThingsWorthwhileScale] [int] NULL 
) ON [PRIMARY] 

回答

2

此腳本爲任何具有類似PK名稱的表生成動態SQL。

查詢:

SET NOCOUNT ON 

IF OBJECT_ID (N'dbo.A') IS NOT NULL 
    DROP TABLE dbo.A 

IF OBJECT_ID (N'dbo.B') IS NOT NULL 
    DROP TABLE dbo.B 

IF OBJECT_ID (N'dbo.C') IS NOT NULL 
    DROP TABLE dbo.C 

CREATE TABLE dbo.A (PK_field INT PRIMARY KEY, column1 INT, column2 INT) 
CREATE TABLE dbo.B (PK_field INT PRIMARY KEY, column3 INT, column4 INT) 
CREATE TABLE dbo.C (PK_field INT PRIMARY KEY, column5 INT, [column 6] INT) 

INSERT INTO dbo.A (PK_field, column1, column2) 
VALUES (1, 1, 2), (2, 1, 2) 

INSERT INTO dbo.B (PK_field, column3, column4) 
VALUES (2, 3, 4) 

INSERT INTO dbo.C (PK_field, column5, [column 6]) 
VALUES (1, 5, 6), (3, 5, 6) 

DECLARE @SQL NVARCHAR(MAX) 

;WITH cte AS 
(
    SELECT 
      column_name = '[' + c.name + ']' 
     , table_name = '[' + s.name + '].[' + o.name + ']' 
    FROM sys.columns c WITH (NOLOCK) 
    JOIN sys.objects o WITH (NOLOCK) ON c.[object_id] = o.[object_id] 
    JOIN sys.schemas s WITH (NOLOCK) ON o.[schema_id] = s.[schema_id] 
    WHERE o.name IN ('A', 'B', 'C') 
     AND s.name = 'dbo' 
     AND o.[type] = 'U' 
), unicol AS (
    SELECT TOP 1 column_name 
    FROM cte 
    GROUP BY cte.column_name 
    HAVING COUNT(cte.column_name) > 1 
), cols AS 
(
    SELECT DISTINCT column_name 
    FROM cte  
), tbl AS 
(
    SELECT DISTINCT table_name 
    FROM cte 
), rs AS 
(
    SELECT 
      tbl.table_name 
     , column_name = ISNULL(cte.column_name, cols.column_name + ' = NULL') 
    FROM cols 
    CROSS JOIN tbl 
    LEFT JOIN cte ON cols.column_name = cte.column_name AND cte.table_name = tbl.table_name 
), rs2 AS (
    SELECT uni = ' UNION ALL' + CHAR(13) + 'SELECT ' + STUFF((
     SELECT ', ' + rs.column_name 
     FROM rs 
     WHERE tbl.table_name = rs.table_name 
     GROUP BY rs.column_name 
     ORDER BY rs.column_name 
     FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + 
     ' FROM ' + table_name 
    FROM tbl 
) 
SELECT @SQL = 'SELECT 
' + STUFF((
    SELECT CHAR(13) + ', ' + ISNULL(unicol.column_name, cols.column_name + ' = MAX(' + cols.column_name + ')') 
    FROM cols 
    LEFT JOIN unicol ON cols.column_name = unicol.column_name 
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') 
+ ' 
FROM 
(' + STUFF((
    SELECT CHAR(10) + uni 
    FROM rs2 
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 11, '') + CHAR(13) + 
    ') t 
GROUP BY ' + (SELECT column_name FROM unicol) 

PRINT @SQL 

EXECUTE sys.sp_executesql @SQL 

輸出:

SELECT 
     [column 6] = MAX([column 6]) 
    , [column1] = MAX([column1]) 
    , [column2] = MAX([column2]) 
    , [column3] = MAX([column3]) 
    , [column4] = MAX([column4]) 
    , [column5] = MAX([column5]) 
    , [PK_field] 
FROM (
    SELECT [column 6] = NULL, [column1], [column2], [column3] = NULL, [column4] = NULL, [column5] = NULL, [PK_field] FROM [dbo].[A] 
    UNION ALL 
    SELECT [column 6] = NULL, [column1] = NULL, [column2] = NULL, [column3], [column4], [column5] = NULL, [PK_field] FROM [dbo].[B] 
    UNION ALL 
    SELECT [column 6], [column1] = NULL, [column2] = NULL, [column3] = NULL, [column4] = NULL, [column5], [PK_field] FROM [dbo].[C] 
) t 
GROUP BY [PK_field] 

結果:

column 6 column1  column2  column3  column4  column5  PK_field 
----------- ----------- ----------- ----------- ----------- ----------- ----------- 
6   1   2   NULL  NULL  5   1 
NULL  1   2   3   4   NULL  2 
6   NULL  NULL  NULL  NULL  5   3 

更新腳本:

DECLARE @SQL NVARCHAR(2000) -> NVARCHAR(MAX) 

輸出爲您的DDL:

SELECT 
    [blaiseKey_code] 
, [bDEM_BOP_q1stParentBornNZ] = MAX([bDEM_BOP_q1stParentBornNZ]) 
, [bDEM_BOP_q2ndParentBornNZ] = MAX([bDEM_BOP_q2ndParentBornNZ]) 
, [bDEM_BOP_qHowManyParentBornNZ] = MAX([bDEM_BOP_qHowManyParentBornNZ]) 
, [bDEM_BOP_qHowManyRaised] = MAX([bDEM_BOP_qHowManyRaised]) 
, [bDEM_WOR_q2Jobs1HrsIntro] = MAX([bDEM_WOR_q2Jobs1HrsIntro]) 
, [bDEM_WOR_q2Jobs2HrsIntro] = MAX([bDEM_WOR_q2Jobs2HrsIntro]) 
, [bDEM_WOR_q2JobsNoHrsIntro] = MAX([bDEM_WOR_q2JobsNoHrsIntro]) 
, [bDEM_WOR_qEmployArrangement] = MAX([bDEM_WOR_qEmployArrangement]) 
, [bDEM_WOR_qFeelAboutJob] = MAX([bDEM_WOR_qFeelAboutJob]) 
, [bDEM_WOR_qJobsNum] = MAX([bDEM_WOR_qJobsNum]) 
, [bDEM_WOR_qJobsNumNR] = MAX([bDEM_WOR_qJobsNumNR]) 
, [bDEM_WOR_qMainTasks] = MAX([bDEM_WOR_qMainTasks]) 
, [bDEM_WOR_qMainTasksNR] = MAX([bDEM_WOR_qMainTasksNR]) 
, [bDEM_WOR_qOccupation] = MAX([bDEM_WOR_qOccupation]) 
, [bDEM_WOR_qOccupationNR] = MAX([bDEM_WOR_qOccupationNR]) 
, [bDEM_WOR_qPaidWorkIntro] = MAX([bDEM_WOR_qPaidWorkIntro]) 
, [bDEM_WOR_qPermEmployee] = MAX([bDEM_WOR_qPermEmployee]) 
, [bDEM_WOR_tabDEM_T2_fTotMins] = MAX([bDEM_WOR_tabDEM_T2_fTotMins]) 
, [fCountryName] = MAX([fCountryName]) 
, [q3MthsStudy] = MAX([q3MthsStudy]) 
, [qAge] = MAX([qAge]) 
, [qAge15OrOver] = MAX([qAge15OrOver]) 
, [qAgeNR] = MAX([qAgeNR]) 
, [qAgeRange] = MAX([qAgeRange]) 
, [qArriveNZMth] = MAX([qArriveNZMth]) 
, [qArriveNZYr] = MAX([qArriveNZYr]) 
, [qArriveNZYrNR] = MAX([qArriveNZYrNR]) 
, [qAwayFromWork] = MAX([qAwayFromWork]) 
, [qBornInNZ] = MAX([qBornInNZ]) 
, [qCouldStartLastWk] = MAX([qCouldStartLastWk]) 
, [qCountryOfBirth] = MAX([qCountryOfBirth]) 
, [qDidPaidWork] = MAX([qDidPaidWork]) 
, [qDOB] = MAX([qDOB]) 
, [qDOBNR] = MAX([qDOBNR]) 
, [qFamilyBusWork] = MAX([qFamilyBusWork]) 
, [qHasJobToStart] = MAX([qHasJobToStart]) 
, [qHighestQual] = MAX([qHighestQual]) 
, [qHighestQualOth] = MAX([qHighestQualOth]) 
, [qHighestQualOthNR] = MAX([qHighestQualOthNR]) 
, [qHighestQualYr] = MAX([qHighestQualYr]) 
, [qHighestQualYrNR] = MAX([qHighestQualYrNR]) 
, [qIncTotalAmt] = MAX([qIncTotalAmt]) 
, [qJobSearchA] = MAX([qJobSearchA]) 
, [qJobSearchB] = MAX([qJobSearchB]) 
, [qJobSearchC] = MAX([qJobSearchC]) 
, [qJobSearchD] = MAX([qJobSearchD]) 
, [qJobSearchE] = MAX([qJobSearchE]) 
, [qJobSearchF] = MAX([qJobSearchF]) 
, [qJobSearchG] = MAX([qJobSearchG]) 
, [qJobSearchH] = MAX([qJobSearchH]) 
, [qJobSearchI] = MAX([qJobSearchI]) 
, [qJobSearchOth] = MAX([qJobSearchOth]) 
, [qJobSearchOthNR] = MAX([qJobSearchOthNR]) 
, [qLookedForWork] = MAX([qLookedForWork]) 
, [qMaoriDescent] = MAX([qMaoriDescent]) 
, [qNotEligible] = MAX([qNotEligible]) 
, [qPostSchQual] = MAX([qPostSchQual]) 
, [qSchQual] = MAX([qSchQual]) 
, [qSchQualOth] = MAX([qSchQualOth]) 
, [qSchQualOthNR] = MAX([qSchQualOthNR]) 
, [qSchQualYr] = MAX([qSchQualYr]) 
, [qSchQualYrNR] = MAX([qSchQualYrNR]) 
, [qSex] = MAX([qSex]) 
, [qThingsWorthwhileScale] = MAX([qThingsWorthwhileScale]) 
, [qWorkIntro] = MAX([qWorkIntro]) 
FROM 
(
SELECT [bDEM_BOP_q1stParentBornNZ], [bDEM_BOP_q2ndParentBornNZ], [bDEM_BOP_qHowManyParentBornNZ], [bDEM_BOP_qHowManyRaised], [bDEM_WOR_q2Jobs1HrsIntro], [bDEM_WOR_q2Jobs2HrsIntro], [bDEM_WOR_q2JobsNoHrsIntro], [bDEM_WOR_qEmployArrangement], [bDEM_WOR_qFeelAboutJob], [bDEM_WOR_qJobsNum], [bDEM_WOR_qJobsNumNR], [bDEM_WOR_qMainTasks], [bDEM_WOR_qMainTasksNR], [bDEM_WOR_qOccupation], [bDEM_WOR_qOccupationNR], [bDEM_WOR_qPaidWorkIntro], [bDEM_WOR_qPermEmployee], [bDEM_WOR_tabDEM_T2_fTotMins], [blaiseKey_code], [fCountryName], [q3MthsStudy], [qAge], [qAge15OrOver], [qAgeNR], [qAgeRange], [qArriveNZMth], [qArriveNZYr], [qArriveNZYrNR], [qAwayFromWork], [qBornInNZ], [qCouldStartLastWk], [qCountryOfBirth], [qDidPaidWork], [qDOB], [qDOBNR], [qFamilyBusWork], [qHasJobToStart], [qHighestQual], [qHighestQualOth], [qHighestQualOthNR], [qHighestQualYr], [qHighestQualYrNR], [qIncTotalAmt], [qJobSearchA], [qJobSearchB], [qJobSearchC], [qJobSearchD], [qJobSearchE], [qJobSearchF], [qJobSearchG], [qJobSearchH], [qJobSearchI], [qJobSearchOth], [qJobSearchOthNR], [qLookedForWork], [qMaoriDescent], [qNotEligible], [qPostSchQual], [qSchQual], [qSchQualOth], [qSchQualOthNR], [qSchQualYr], [qSchQualYrNR], [qSex], [qThingsWorthwhileScale] = NULL, [qWorkIntro] FROM [dbo].[G_bDEM] 
UNION ALL 
SELECT [bDEM_BOP_q1stParentBornNZ] = NULL, [bDEM_BOP_q2ndParentBornNZ] = NULL, [bDEM_BOP_qHowManyParentBornNZ] = NULL, [bDEM_BOP_qHowManyRaised] = NULL, [bDEM_WOR_q2Jobs1HrsIntro] = NULL, [bDEM_WOR_q2Jobs2HrsIntro] = NULL, [bDEM_WOR_q2JobsNoHrsIntro] = NULL, [bDEM_WOR_qEmployArrangement] = NULL, [bDEM_WOR_qFeelAboutJob] = NULL, [bDEM_WOR_qJobsNum] = NULL, [bDEM_WOR_qJobsNumNR] = NULL, [bDEM_WOR_qMainTasks] = NULL, [bDEM_WOR_qMainTasksNR] = NULL, [bDEM_WOR_qOccupation] = NULL, [bDEM_WOR_qOccupationNR] = NULL, [bDEM_WOR_qPaidWorkIntro] = NULL, [bDEM_WOR_qPermEmployee] = NULL, [bDEM_WOR_tabDEM_T2_fTotMins] = NULL, [blaiseKey_code], [fCountryName] = NULL, [q3MthsStudy] = NULL, [qAge] = NULL, [qAge15OrOver] = NULL, [qAgeNR] = NULL, [qAgeRange] = NULL, [qArriveNZMth] = NULL, [qArriveNZYr] = NULL, [qArriveNZYrNR] = NULL, [qAwayFromWork] = NULL, [qBornInNZ] = NULL, [qCouldStartLastWk] = NULL, [qCountryOfBirth] = NULL, [qDidPaidWork] = NULL, [qDOB] = NULL, [qDOBNR] = NULL, [qFamilyBusWork] = NULL, [qHasJobToStart] = NULL, [qHighestQual] = NULL, [qHighestQualOth] = NULL, [qHighestQualOthNR] = NULL, [qHighestQualYr] = NULL, [qHighestQualYrNR] = NULL, [qIncTotalAmt] = NULL, [qJobSearchA] = NULL, [qJobSearchB] = NULL, [qJobSearchC] = NULL, [qJobSearchD] = NULL, [qJobSearchE] = NULL, [qJobSearchF] = NULL, [qJobSearchG] = NULL, [qJobSearchH] = NULL, [qJobSearchI] = NULL, [qJobSearchOth] = NULL, [qJobSearchOthNR] = NULL, [qLookedForWork] = NULL, [qMaoriDescent] = NULL, [qNotEligible] = NULL, [qPostSchQual] = NULL, [qSchQual] = NULL, [qSchQualOth] = NULL, [qSchQualOthNR] = NULL, [qSchQualYr] = NULL, [qSchQualYrNR] = NULL, [qSex] = NULL, [qThingsWorthwhileScale], [qWorkIntro] = NULL FROM [dbo].[G_bLWW] 
) t 
GROUP BY [blaiseKey_code] 
+0

嗨@Devart,你的解決方案看起來很酷!但是,當我執行代碼時,我有一個錯誤「字符串後面的未封閉引號」。列名沒有任何特殊字符,我想知道什麼是停止查詢? – nzsquall 2013-05-15 20:52:35

+0

錯誤消息顯示如下: 'SELECT [bDEM_BOP_q1stParentBornNZ] = MAX([bDEM_BOP_q1stParentBornNZ]) ,[bDEM_BOP_q2ndParentBornNZ] = MAX([bDEM_BOP_q2ndParentBornNZ]) ,[bDEM_BOP_qHowManyParentBornNZ] = MAX([bDEM_BOP_qHowManyParentBornNZ])....在字符串'qHighestQual'後面未使用引號。 Msg 102,Level 15,State 1,Line 2 'qHighestQual'附近的語法不正確 – nzsquall 2013-05-15 20:54:07

+0

我在問題描述中提供了DDL,謝謝。 – nzsquall 2013-05-16 20:50:22

0

你只能做到這一點作爲一個存儲過程。一個SQL查詢返回一組指定的列,不多不少。獲得可變數量列的唯一方法是使用動態SQL。而且,函數不支持動態SQL。

您需要構造一個SQL語句,並將INFORMATION_SCHEMA.Columns中的列名連接起來。事情是這樣的:

declare @cols varchar(max); 

select @cols = stuff((select distinct ', ['+c.column_name+']' 
         from INFORMATION_SCHEMA.Columns c 
         where c.table_name in (<list of tables here>) 
         for xml path ('') 
        ), 1, 2, ''); 

這將不具有特殊的HTML字符的列名,如「<」,「>」或「&」工作。

然後,您可以構建完整的查詢語句並執行它,使用exec()sp_executesql()

另一種方法是創建一個包含所有連接和所有列的視圖。讓SQL優化器確定最佳執行路徑。

1

試試這個:

DECLARE 
    @cols VARCHAR(MAX) 
    , @TableA VARCHAR(10)= 'TableA' 
    , @TableB VARCHAR(10)= 'TableB' 
    , @TableC VARCHAR(10)= 'TableC' 
    , @Pk VARCHAR(20) 

SELECT 
    @cols = STUFF((
      SELECT DISTINCT ', [' + c.column_name + ']' 
      FROM INFORMATION_SCHEMA.Columns c 
      WHERE c.table_name IN (@TableA,@TableB,@TableC) 
      FOR XML PATH('') 
     ), 1, 2, ''); 

SELECT @Pk = column_name 
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 
    AND table_name = @TableA 

DECLARE @query VARCHAR(1000) 
SET @query = 'SELECT ' + @cols + ' FROM ' + @TableA + ' JOIN ' + @TableB 
    + ' ON ' + @TableA + '.' + @Pk + '=' + @TableB + '.' + @Pk 
    + ' JOIN ' + @TableC + ' ON ' + @TableB + '.' + @Pk + '=' + @TableC 
    + '.' + @Pk 

EXEC (@query) 

不要忘記通過@Gordon給出列名特殊的HTML字符的警告。

+0

@Eric:這是否幫助? – 2013-05-10 11:32:34

+0

嗨,當我在桌子上執行這個聲明時,什麼都沒有回來?我在EXEC聲明前打印@query,並注意到它也回來了。 – nzsquall 2013-05-15 20:48:24

+0

@Eric:你在'@ cols'或'@ pk'中得到了什麼? – 2013-05-16 05:26:12