我正在处理一个存储过程,它为每个公司获取数据并执行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运行此服务)。
答案 0 :(得分:0)
但是,如果你不能,你可以使用参数化top (x) percent
来避免动态SQL和大量重复代码,你可以在100
或0
指定,具体取决于是您想要从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