替代CROSS APPLY

时间:2016-09-13 23:54:04

标签: sql-server

我有一个存储过程,用于将多行数据压缩到一个变量中,如下所示:

DECLARE @Description VARCHAR(200);

SELECT @Description = COALESCE(@Description) + ', ', '') + [Description]
FROM dbo.DrugDescriptionAndImprints
WHERE NDC = @NDC;

我的问题是,我最近需要同时为多个NDC完成此操作以生成报告数据。

我目前的解决方案是将CROSS APPLY与表值函数一起使用,但由于性能下降,这不是最优的。我希望能够'#34; flatten"视图中的这些数据,但我无法在视图中使用变量。有没有办法在不使用CROSS APPLY和表值函数的情况下完成此任务?

这是表值函数供参考:

CREATE FUNCTION [dbo].[fnFlatDescriptionByNdc]
(
    @NDC VARCHAR(11)
)
RETURNS 
@ret TABLE 
(
    [Description] VARCHAR(200)
)
AS
BEGIN
    DECLARE @Description VARCHAR(200)
    DECLARE @Imprint1 VARCHAR(40)
    DECLARE @Imprint2 VARCHAR(40)

    SELECT @Description = Coalesce(@Description + ', ', '') + [Description]
    FROM dbo.DrugDescriptionAndImprints WHERE NDC = @NDC

    SELECT @Imprint1 = COALESCE(Imprint1, '')
    FROM dbo.DrugDescriptionAndImprints WHERE NDC = @NDC 
    ORDER BY Imprint1 DESC

    SELECT @Imprint2 = COALESCE(Imprint2, '')
    FROM dbo.DrugDescriptionAndImprints WHERE NDC = @NDC 
    ORDER BY Imprint2 DESC

    INSERT INTO @ret ([Description])
    SELECT @Description + CASE WHEN @Imprint1 <> '' OR @Imprint2 <> '' THEN ' (' +
        CASE WHEN @Imprint1 <> '' THEN @Imprint1 + 
            CASE WHEN @Imprint2 <> '' THEN ' / ' + @Imprint2
            ELSE ''
            END
        ELSE @Imprint2 END + ')' ELSE '' END

    RETURN 
END

我正在使用SQL Server 2008 R2。

修改 以下是一些示例数据,以显示我正在使用的内容:

NDC              ProdDescAbbr           Description        Imprint1      Imprint2
00005250033      FIBERCON   TAB 625MG   film-coated        LL            F 1
00005250033      FIBERCON   TAB 625MG   tan                LL            F 1
00005250033      FIBERCON   TAB 625MG   scored             LL            F 1
00005250033      FIBERCON   TAB 625MG   oblong             LL            F 1

1 个答案:

答案 0 :(得分:1)

你对现在正在做什么以及你想做什么的解释有点令人困惑,但我将解决我所看到的问题。

我认为你说你不想使用用户定义的功能,这很好。 UDF没有性能提升,事实上,如果您使用除了内联表函数之外的任何东西,您将看到性能损失。 UDF唯一真正的优势是代码重用,这对于已经优化并在各处使用的内联表函数非常有用。但是,我们假设这不属于该类别。如果确实需要实现UDF,请花时间了解如何以基于集合的方式执行操作,以便可以使用内联表函数。我在下面提供的代码可以很容易地修改为在内联表函数中工作。

您引用了CROSS APPLY,您在任何提供的代码中都没有使用它。我不确定你是否意味着你正在使用CROSS APPLY加入该函数。在任何情况下,我的理解是你想要离开函数,以便能够同时获得多个记录。我不知道你的数据,但我猜这是一个很好的电话,你可以通过同时在多个NDC上完成这项工作来获得更好的性能。

我创建了一些测试数据(不仅仅是一个NDC)并且稍微改变了一些以便能够验证一切是如何工作的:

DECLARE @myTable TABLE
    ( tableId INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , NDC VARCHAR(11) NOT NULL 
    , ProdDescAbbr VARCHAR(30) NOT NULL 
    , [Description] VARCHAR(30) NOT NULL
    , Imprint1 VARCHAR(5) NOT NULL
    , Imprint2 VARCHAR(5) NOT NULL );

INSERT INTO @myTable VALUES
    ('00005250033', 'FIBERCON   TAB 625MG', 'film-coated', 'LL', 'F 1' ),
    ('00005250033', 'FIBERCON   TAB 625MG', 'tan', 'LL', 'F 1' ),
    ('00005250033', 'FIBERCON   TAB 625MG', 'scored', 'LQ', 'F 1' ),
    ('00005250033', 'FIBERCON   TAB 625MG', 'oblong', 'LL', 'F 2' ),
    ('00005250034', 'FIBERCON   TAB 625MG', 'short', '', '' ),
    ('00005250034', 'FIBERCON   TAB 625MG', 'green', '', '' ),
    ('00005250035', 'FIBERCON   TAB 625MG', 'open', '', '' ),
    ('00005250035', 'FIBERCON   TAB 625MG', 'yes', '', '' );

希望你桌上有一些主键......我冒昧地创造了一个。另外,用最大值来获取印记可能有点奇怪,而不是使用具有可调先例值的某种查找表,但我们不会花时间评估结构,因为这不是问题的真正含义。 / p>

在这个例子中(因为我没有你正在使用函数结果的示例查询)我使用cte'cteTemp'来表示在父查询中定义你想要哪个NDC的任何内容知道并加入源数据...在我的例子中的@myTable和你系统中的dbo.DrugDescriptionAndImprints:

WITH cteTemp AS
    (
    SELECT *
    FROM @myTable
    WHERE NDC IN ('00005250033', '00005250035')
    )
, cteImprints AS
    (
    SELECT  cte.NDC
        ,   Imprint1 = MAX(cte.Imprint1)
        ,   Imprint2 = MAX(cte.Imprint2)
    FROM cteTemp AS cte
    GROUP BY cte.NDC
    )
, cteFinal AS
    (
    SELECT  cte.NDC
        ,   Descriptions = STUFF(CONVERT(VARCHAR(1000), 
            (SELECT ',' + sq.[Description] 
            FROM cteTemp AS sq 
            WHERE sq.NDC = cte.NDC 
            ORDER BY sq.[Description] 
            FOR XML PATH (''))), 1, 1, '')
        ,   cte.Imprint1
        ,   cte.Imprint2
    FROM cteImprints AS cte
    )
SELECT  cte.NDC
    ,   concatenatedString = 
        cte.Descriptions + CASE WHEN cte.Imprint1 <> '' OR cte.Imprint2 <> '' THEN ' (' +
        CASE WHEN cte.Imprint1 <> '' THEN cte.Imprint1 + 
            CASE WHEN cte.Imprint2 <> '' THEN ' / ' + cte.Imprint2
            ELSE ''
            END
        ELSE cte.Imprint2 END + ')' ELSE '' END
FROM cteFinal AS cte;

存在一些潜在的性能问题。理想情况下,我们只会从源表(@myTable / dbo.DrugDescriptionAndImprints)读取一次并从那里做所有事情。这里的关键点是我们可以在一个查询中获得最大的印记,但我认为我们不能避免需要单独的传递来获取我们想要的每个NDC的描述连接。您可以使用表变量或临时表来避免返回源,但通常这比接受返回行程更糟糕。但您必须在系统上对此进行测试并评估执行计划,以确定它是否符合您的性能标准。