【问题标题】:Stored procedure logic for select选择的存储过程逻辑
【发布时间】:2022-01-06 00:13:30
【问题描述】:

我有以下程序:

CREATE PROCEDURE sp_types
    @type varchar(100)
AS
BEGIN
    DECLARE @products table(productId int)

    IF @type = 'Merchandise'
    BEGIN
        INSERT INTO @products
            SELECT productId
            FROM dbo.product
            WHERE type = @type
    END
    ELSE IF @type = 'Electronics'
    BEGIN
        INSERT INTO @products
            SELECT productId
            FROM dbo.product
            WHERE type = @type
    END
    ELSE IF @type = 'Home'
    BEGIN
        INSERT INTO @products
            SELECT productId
            FROM dbo.product
            WHERE type = @type
    END
    ELSE
    BEGIN
        INSERT INTO @products
            SELECT productId
            FROM dbo.product
            WHERE type = @type
    END

    /* here we have logic to convert all the productids in the @products table into an XML format
    <products>
      <productId>1</productId>
      <productId>2</productId>
    ....
    ....
    ....
      <productId>100</productId>
    </products>
    */

    /* after we get the XML string, it is passed to another procedure to print out details about the products */
    EXEC sp_products_list @xml = @productXml

END /* procedure ends here */

这是sp_products_list 过程:

CREATE PROCEDURE sp_products_list
    @xml XML
AS
BEGIN
    DECLARE @products TABLE (productId int)

    INSERT INTO @products
        SELECT @xml.value('productId','int')
        FROM @xml.nodes('products')

    /* Select statement */
    SELECT
        a.productId, a.productName, 
        b.productRegion, b.ProductQuantity, 
        c.productSupplier
    FROM
        products a
    JOIN 
        productRegion b ON a.productid = b.productid
    JOIN 
        productSupplier c ON c.productRegion = b.productRegion
    WHERE 
        a.productId IN (SELECT productId FROM @products)

END /* procedure end */

sp_products_list 由除sp_types 过程之外的许多其他过程调用。我有一个要求,当我将一种“商品”传递给sp_types 过程时,我需要显示一些额外的列,例如productSupplierRegionproductSupplierCount 等。

但是对于其余的类型,我只需要显示sp_products_list过程中的select语句当前显示的内容。

如果我只是将我需要的列添加到当前 sp_products_list 过程中的 select 语句中,那么它们将显示为传递给 sp_types 过程的任何类型,这不是我想要的。

我的解决方案:我能想到的解决方案之一是在sp_products_list 过程中接收一个@type 变量作为输入,并为select 语句添加一个if-else 语句。如果传入了“商品”的类型,则显示带有附加列的选择,否则显示常规列。

我将来使用这种方法可能面临的问题是,如果我们想为传入的不同 @type 变量添加不同类型的列。在这种情况下,我将不得不做多个 if -else 每种类型的语句。我计划使用动态 SQL,但我的想法被否决了,因为我的团队不是动态 SQL 的忠实拥护者。

现在我正在尝试为这个问题找到一个可能在任何情况下都有效的可靠解决方案。有什么想法或建议吗?谢谢!

【问题讨论】:

  • 作为一项规则,存储过程不应返回具有不同列集、数据类型、结果集数量...的结果,具体取决于输入参数、月份时间、磁盘容量... ...有时它是有道理的,但很少。它要求使用数据的应用程序能够以合理的方式确定如何处理产生的一堆'o'bits。旁白:似乎所有处理@type 值的if/then 代码对预期值执行完全相同的操作,但以相同方式处理意外值。你没有告诉我们什么?
  • 未来根据你的需求,你有多种类型,每种类型需要不同的列列表,那么你应该将类型和列详细信息存储在一个表中,并根据类型获取列详细信息,并且把它们放在一个选择语句上。不,需要任何 if else 语句。
  • 旁注:您应该为您的存储过程使用sp_ 前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免sp_ 并使用其他东西作为前缀 - 或者根本不使用前缀!
  • 旁白:我建议您使用表类型在存储过程之间传递数据,这可能比 XML 更有效。尽管您可以使用 XML 来解决当前的问题:仅在某些时候适用的额外列,但是您会将其作为表类型的 XML 列,而不是作为每一行的整个 blob 一起

标签: sql sql-server database tsql stored-procedures


【解决方案1】:

我建议您将sp_products_list 重写为表值函数,然后您可以将它与您需要的任何额外列连接起来

CREATE OR ALTER FUNCTION dbo.products_list ( @productId int )
RETURNS TABLE    
AS RETURN

    SELECT
        p.productId, p.productName, 
        pr.productRegion, pr.ProductQuantity, 
        ps.productSupplier
    FROM
        products p
    JOIN 
        productRegion pr ON p.productid = pr.productid
    JOIN 
        productSupplier ps ON ps.productRegion = pr.productRegion
    WHERE 
        p.productId = @productId;

GO

然后您只需在每个过程中使用该函数即可。例如。

CREATE PROCEDURE ProductByType
    @type varchar(100)
AS

    IF @type = 'Merchandise'
    BEGIN
        SELECT pl.*, p.OtherColumns, t.OtherTablesColumns
            FROM dbo.product p
            CROSS APPLY dbo.products_list (p.productId) pl
            LEFT JOIN OtherTable t ON SomeCondition
            WHERE p.type = @type;
    END;
    ELSE
    BEGIN
        SELECT pl.*
            FROM dbo.product p
            CROSS APPLY dbo.products_list (p.productId) pl
            WHERE p.type = @type;
    END;

您可能会发现将@type 选择推送到函数中会更高效。然后你可以简单地做

SELECT pl.*
FROM dbo.products_list_by_type (@type) pl

服务器将从查询计划中删除所有不需要的列。

【讨论】:

  • 非常感谢!如果我的结果集有 100k 行怎么办?如果我使用 TVF 是否会影响性能,因为必须为每一行进行函数调用?
  • 不,一个 inline 表值函数(像这样的单个 AS RETURN SELECT)被编译到外部查询中,类似于视图。正如我最后所说,如果您将@type 条件推送到函数中,选择您将需要的所有列,您可能会发现更好的性能。服务器将从最终查询计划中删除不必要的列。无论哪种方式,性能肯定会比你已经拥有的更好。我希望你的索引是好的:你需要在type上的索引
猜你喜欢
  • 2011-11-04
  • 2011-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-30
相关资源
最近更新 更多