基于参数的联合动态查询

时间:2016-12-01 15:20:24

标签: sql-server tsql sql-server-2012

我正在处理一个存储过程,它为每个公司获取数据并执行SELECT INTO语句。目前,我有一个if条件,它只适用于选定的公司。但是,在此之后我有许多Union语句将SELECT INTO创建的所有表联合起来:

SELECT * INTO ##LevelAll FROM ##Levela
UNION
SELECT * FROM ##Levelb 
UNION
SELECT * FROM ##Levelc
UNION
SELECT * FROM ##Leveld
UNION
SELECT * FROM ##Levele

我遇到的问题是,如果我没有运行级别c的查询,那么上面的代码将会失败。

有没有办法可以动态查询...根据我运行存储过程4的公司? (我可以为多家公司&CompanyA,CompanyB,CompanyC运行此服务)。

2 个答案:

答案 0 :(得分:0)

正如评论中所提到的,只需更改数据库结构就可以从中受益。

但是,如果你不能,你可以使用参数化top (x) percent来避免动态SQL和大量重复代码,你可以在1000指定,具体取决于是您想要从union的那部分返回数据。

我还会说,看起来你似乎并不关心这个问题中的重复项,因为它的水平是'是不同的公司,因此您可以使用union all代替union,这将加快您的效果:

declare @a int = 100
        ,@b int = 100
        ,@c int = 0     -- No records from table c will be returned.
        ,@d int = 100
        ,@e int = 100;

SELECT TOP (@a) PERCENT * 
INTO ##LevelAll
FROM ##Levela
UNION ALL
SELECT TOP (@b) PERCENT * 
FROM ##Levelb
UNION ALL
SELECT TOP (@c) PERCENT * 
FROM ##Levelc
UNION ALL
SELECT TOP (@d) PERCENT * 
FROM ##Leveld
UNION ALL
SELECT TOP (@e) PERCENT * 
FROM ##Levele;

答案 1 :(得分:0)

我已经基于通过存储过程传递的变量构建了动态查询。我的文字被评论为发生了什么。

首先,您需要一个表值函数来拆分参数,以便其中列出的每个公司都是生成的返回表中的一行。下面是一个函数,您需要在数据库中创建它:

CREATE FUNCTION [dbo].[ufnSplit] 
( 
    @string VARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(Item VARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (Item)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

接下来,在存储过程中,我们使用此函数将参数拆分为行,并将其分成名为#Companies的临时表。然后,我们使用循环来完成#Companies来创建UNION个查询(如果只有一个值,则不会)。我再一次对这个问题做了很多评论。

USE [DatabaseName]
GO

CREATE PROCEDURE uspLevelReports
 @param VARCHAR(MAX)

/* Use this for testing your parameters: 
EXEC uspLevelReports @param = 'A,B,D'
 */

AS

BEGIN TRY


IF OBJECT_ID('tempdb..#Companies') IS NOT NULL
 DROP TABLE #Companies

-- use the split function to put each company into a table, and assign it a row number (necessary for the loop below)
SELECT
Item
,ROW_NUMBER() OVER(ORDER BY Item ASC) AS RowNo
INTO #Companies
FROM dbo.ufnSplit(@param,',')


-- Select below is for testing so you can see the effect:
-- SELECT * FROM #Companies

-- Create the basic query here, with the company suffix
DECLARE @sql NVARCHAR(4000) =  'SELECT * From tempdb..##Level'

-- Start to loop through #Companies, for each row no higher than 1, a union will be added
-- First, tell the loop where to start and end:
DECLARE @RowNo INT = 1 -- Your starting RowNo, should always be 1 so won't do a MIN() here
DECLARE @MaxNo INT = ( -- The highest RowNo in #Companies
    SELECT 
    MAX(RowNo)
    FROM #Companies
    )
DECLARE @originalSQL  NVARCHAR(4000) = @sql -- @Sql will be modified in the loop but we still need the original form to keep adding to each union if one is there

WHILE @RowNo <= @MaxNo  -- we will increase the value of @RowNo by 1 each time,the loop will break when @RowNo gets higher than @MaxNo
BEGIN

-- Find the company that corresponds to the @RowNo value in #Companies and add it to a variable:
DECLARE @suffix VARCHAR(100) = (
    SELECT 
    Item 
    FROM #Companies 
    WHERE RowNo = @RowNo)

IF @RowNo = @MaxNo -- If statement for the final Item in #Companies, this stops us adding UNION where we've finished the query
    SET @sql += @suffix
ELSE
    SET @sql += @suffix + ' UNION ' + @originalSQL;

SET @RowNo += 1 -- Add one to move to next RowNo in #Companies

PRINT @sql -- This is for testing so you can see in Messages how the query is being built each loop

END

PRINT 'Final query: ' + @sql  -- This is for testing so you can see in Messages what your final query is



-- Create your level all table
IF OBJECT_ID('tempdb..##LevelAll') IS NOT NULL
 DROP TABLE ##LevelAll

-- You need to create your ##LevelAll table with all the columns needed, so amend them with their datatypes below mine is just for example:

CREATE TABLE ##LevelAll(
Company CHAR(1)
,value INT
)
--- insert into your level all table
INSERT INTO ##LevelAll
-- by executing the statement inside your string variable
exec sp_executesql @sql
-- publish the results
SELECT * FROM ##LevelAll

END TRY

BEGIN CATCH

    -- Your error trapping variables

END CATCH
相关问题