具有多个查询和不同选定列的存储函数

时间:2017-01-19 11:20:14

标签: sql sql-server sql-server-2008

我根据报告类型进行了一系列查询。为简单起见,这是我正在尝试做的一个例子:

If @Reporttype = '1'
Select lcustomerid, lname, fname
from customers
Where dtcreated > @startdate

Else if @Reporttype = '2'
Select barcode, lname, fname 
from employees
where dtcreated > @startdate

Else if @reporttype = '3'
Select thetime, lname, name, barcode, lcustomerid
from Customers
where dtcreated > @startdate

根据传递的报告类型,您会注意到我运行了3个单独的查询。您还会注意到我正在返回不同的列和列数。

我想将它设为存储函数,并根据我传递的报告类型返回我需要的列。但是,我知道由于列数和列名不同 - 这不会像我希望的那样作为存储函数工作。

这里的主要问题是报告此信息 - 我不想拥有单独的功能,因为我必须为每种报告类型维护不同的报告。

我有办法让这项工作成功吗?

6 个答案:

答案 0 :(得分:7)

您可以使用多语句功能,但需要指定将由3个select语句返回的所有列。似乎不可能返回多个结果集。

  

用户定义的函数不能返回多个结果集。用一个   存储过程如果需要返回多个结果集。 https://msdn.microsoft.com/en-us/library/ms191320.aspx

这是一个不便,但在报告中,您只能使用您需要的列,其他列将为空。

CREATE FUNCTION MyFun
(
    @Reporttype int,
    @startdate datetime
)
RETURNS 
@Result TABLE 
(
    lcustomerid int, 
    lname nvarchar(50),
    fname nvarchar(50),
    barcode int,
    thetime datetime,
    name nvarchar(50)
)
AS
BEGIN
    If @Reporttype = '1'
        insert into @Result (lcustomerid, lname, fname)
        select lcustomerid, lname, fname
        from customers
        Where dtcreated > @startdate

    Else if @Reporttype = '2'
        insert into @Result (barcode, lname, fname)
        Select barcode, lname, fname 
        from employees
        where dtcreated > @startdate

    Else if @reporttype = '3'
        insert into @Result (thetime, lname, name, barcode, lcustomerid)
        Select thetime, lname, name, barcode, lcustomerid
        from customers
        where dtcreated > @startdate

    RETURN 
END

所以,你可以用这种方式调用函数

SELECT * FROM dbo.MyFun (1, getdate())

答案 1 :(得分:0)

在SQL中很难创建类似于通用或抽象的东西,特别是当它与SELECT of colums有关时。如果您的目的是编写尽可能少的代码,以便轻松维护sql脚本,并且能够在将来添加新的报表类型,只需稍作更改,我建议使用带有动态sql的存储过程。当您将SELECT视为动态时,您无法使用函数,这是错误的方法。我会写那样的东西

CREATE PROCEDURE MyProcedure
(
@ReportType int,
@startdate datetime
)
AS

BEGIN
DECLARE @colNames varchar(MAX),@tblName varchar(MAX),@sSQL varchar(MAX);
SELECT @colNames = CASE 
                    WHEN @ReportType = 1 THEN
                     'lcustomerid, lname, fname' --You can also add alias
                     WHEN @ReportType = 2 THEN
                     'barcode, lname, fname'
                     WHEN @ReportType = 3 THEN
                     'thetime, lname, name, barcode, lcustomerid'
                     ELSE
                     RAISEERROR('Error msg');
                    END,
        @tblName = CASE 
                    WHEN @ReportType = 1 OR @ReportType = 3 THEN
                     'customers' --You can also add alias
                    WHEN @ReportType = 2 THEN
                     'employees'
                     ELSE
                     RAISEERROR('Error msg');
                    END

SET @sSQL = 
'Select '+@colNames+'
from '+@tblName +'
where dtcreated > '''+CONVERT(varchar(10), @startdate, 121)+''''

EXEC(@sSQL)
END

你会将其称为

EXEC MyProcedure 1,'20170131' 
例如,

使用此代码每次需要新的报告类型时,您需要添加另一行以防所需的列名称。我使用这种方式处理Crystal报表,我认为这是最好的解决方案

答案 2 :(得分:0)

如果您无法使用存储过程并且需要使用某个功能,则可以UNPIVOT数据,而不是client side中的数据PIVOT

当不同数量的列返回到SQL Server Reporting Services报告时,我需要执行类似的操作。例如,以下代码始终返回三列 - RowIDColumnValue

DECLARE @Table01 TABLE
(
    [ID] INT
   ,[Value01] INT
   ,[Value02] NVARCHAR(256)
   ,[Value03] SMALLINT
);

DECLARE @Table02 TABLE
(
    [ID] INT
   ,[Value01] INT
);

INSERT INTO @Table01 ([ID], [Value01], [Value02], [Value03])
VALUES (1, 111, '1V2', 7)
      ,(2, 222, '2V2', 8)
      ,(3, 333, '3V2', 9);

INSERT INTO @Table02 ([ID], [Value01])
VALUES (1, 111)
      ,(2, 222)
      ,(3, 333);


-- your function starts here

DECLARE @Mode SYSNAME = 'Table01' -- try with 'Table02', too

DECLARE @ResultSet TABLE
(
    [RowID] INT
   ,[Column] SYSNAME
   ,[Value] NVARCHAR(128)
);

IF @Mode = 'Table01'
    BEGIN;
        INSERT INTO @ResultSet ([RowID], [Column], [Value])
        SELECT [ID]
              ,[Column]
              ,[Value]
        FROM
        (
            SELECT [ID]
                  ,CAST([Value01] AS NVARCHAR(256))
                  ,CAST([Value02] AS NVARCHAR(256))
                  ,CAST([Value03] AS NVARCHAR(256))
            FROM @Table01
        ) DS ([ID], [Value01], [Value02], [Value03])
        UNPIVOT
        (
            [Value] FOR [Column] IN ([Value01], [Value02], [Value03])
        ) UNPVT
    END;
ELSE
    BEGIN;
        INSERT INTO @ResultSet ([RowID], [Column], [Value])
        SELECT [ID]
              ,[Column]
              ,[Value]
        FROM
        (
            SELECT [ID]
                  ,CAST([Value01] AS NVARCHAR(256))
            FROM @Table02
        ) DS ([ID], [Value01])
        UNPIVOT
        (
            [Value] FOR [Column] IN ([Value01])
        ) UNPVT
    END;

SELECT *
FROM @ResultSet;

然后在报告中我需要再次执行pivot操作。这是一种解决方法,有许多限制:

  • 必须将unpivot数据强制转换为其最大类型(通常为字符串)
  • 执行不必要的操作(pivot - > unpivot),而不仅仅是渲染数据;
  • 它不能很好地处理大量数据(很慢)

和其他人..

答案 3 :(得分:0)

对于此,您可以创建一个标量值函数,该函数返回xml类型列,然后您可以将该xml标记值填充到报告屏幕

CREATE FUNCTION ReportFunc
(
    @intReporttype int,
    @dtStartdate datetime
)
RETURNS XML
BEGIN

Declare @xmlResult xml 

    If @intReporttype = '1'
        SET @xmlResult = (
                select lcustomerid, lname, fname
                from customers
                Where dtcreated > @dtStartdate
                FOR XML PATH (''), TYPE  
                )

    Else if @intReporttype = '2'
        SET @xmlResult = (
                    Select barcode, lname, fname 
                    from employees
                    where dtcreated > @dtStartdate
                    FOR XML PATH (''), TYPE  
                    )

    Else if @intReporttype = '3'
        SET @xmlResult = (  
                    Select thetime, lname, name, barcode, lcustomerid
                    from customers
                    where dtcreated > @dtStartdate
                    FOR XML PATH (''), TYPE  
                    )

    RETURN @xmlResult
END

答案 4 :(得分:0)

如果您可以使用存储过程然后为了可维护性,我会考虑使用一个调用其他存储过程的主存储过程来返回不同的结果集:

CREATE PROCEDURE MyProc_1(@startdate DateTime)
AS
BEGIN
    SELECT lcustomerid, lname, fname 
    FROM customers WHERE dtcreated > @startdate 

END
GO
CREATE PROCEDURE MyProc_2(@startdate DateTime)
AS
BEGIN
    SELECT barcode, lname, fname 
    FROM employees where dtcreated > @startdate 
END
GO
CREATE PROCEDURE MyProc_3(@startdate DateTime)
AS
BEGIN
    SELECT thetime, lname, name, barcode, lcustomerid 
    FROM Customers WHERE dtcreated > @startdate
END
GO

CREATE PROCEDURE MyProc(@Reporttype char(1), @startdate DateTime)
AS 
BEGIN
    IF @Reporttype = '1' EXEC MyProc_1 @startdate
    ELSE IF @Reporttype = '2'  EXEC MyProc_2 @startdate
    ELSE IF @reporttype = '3'  EXEC MyProc_3 @startdate
END
GO

使用:

DECLARE @dt datetime = getdate()
EXEC MyProc 1, @dt

答案 5 :(得分:0)

CREATE Proc Emp_det 
(
@Reporttype INT,
@startdate DATETIME
)
AS
BEGIN
    If @Reporttype = '1' BEGIN

        Select lcustomerid, lname, fname
        FROM customers
        WHERE dtcreated > @startdate

    END 
    ELSE IF @Reporttype = '2' BEGIN

        Select barcode, lname, fname 
        FROM employees
        WHERE dtcreated > @startdate
    END
    ELSE IF @reporttype = '3' BEGIN
        Select thetime, lname, name, barcode, lcustomerid
        FROM Customers
        WHERE dtcreated > @startdate    

    END
END
GO

Exec Emp_det 1,GETDATE()