如何在SELECT语句中对函数产生影响时替换CURSOR

时间:2018-02-14 12:14:16

标签: sql sql-server cursor sql-server-2014

请考虑以下代码:

Declare @MyMinMaxTable Table
(
    [Min]    int,
    [Max]    int,
    [Desc]   NVARCHAR(50)
)

Insert into @MyMinMaxTable
values (0,12,N'Child'),
       (13,19,N'Teenager'),
       (20,25,N'Youth'),
       (25,40,N'Middle-aged'),
       (40,99,N'Old')

Declare @MyTable Table
(
    Id         int identity(1,1),
    [Year]     int,
    Age        int,
    MyCol2     int,
    MyCol3     int null
)

Insert into @MyTable
([Year], Age, MyCol2, MyCol3)
values 
(2012, 10, 1 , 1),
(2012, 28, 2 , 3),
(2012, 14, 1 , 7),
(2012, 24, 3 , 3),
(2012, 80, 1 , 6),
(2012, 39, 1 , 3),
(2012, 45, 1 , 5),
(2012, 23, 2 , 6),
(2012, 72, 3 , 8),
(2012, 17, 1 , null),
(2012, 62, 4 , 9),
(2012, 20, 1 , null),
(2012, 5, 1 , 9),
(2012, 8, 1 , 9),
(2012, 25, 1 , null),
(2012, 41, 2 , 2),
(2012, 26, 1 , 2),
(2012, 33, 4 , 2),
(2012, 40, 1 , 2),
(2012, 33, 2 , 3),
(2012, 41, 1 , 5),
(2012, 53, 1 , null),
(2012, 37, 1 , 3)

Declare @Result Table
(
    C0         NVARCHAR(50),
    c1         decimal(5,2),
    C2         decimal(5,2),
    C3         decimal(5,2)
)

CURSOR部分:

DECLARE @Min    int;
DECLARE @Max    int;
DECLARE @Desc   nvarchar(50);

DECLARE mycur CURSOR  
FOR
    SELECT [min],
           [max],
           [Desc]
    FROM   @MyMinMaxTable
OPEN mycur

FETCH NEXT FROM mycur INTO @Min, @Max, @Desc

WHILE (@@fetch_status = 0)
    BEGIN
        INSERT INTO @Result
        SELECT @Desc As c0,
               (Cast(COUNT(CASE when Age >= @Min AND Age <= @Max  THEN 1 END) as decimal(5,2)) / cast(COUNT(Id) as decimal(5,2))) As c1,
               (Cast(COUNT(CASE when MyCol2 = 1 AND MyCol3 IS NOT NULL THEN 1 END) as decimal(5,2))  / cast(COUNT(CASE when Age >= @Min AND Age <= @Max  THEN 1 END) as decimal(5,2))) As c2,
               (Cast(COUNT(CASE when Age >= @Min AND Age <= @Max  ANd MyCol2 = 1 THEN 1 END) as decimal(5,2)) / cast(COUNT(CASE when MyCol2 = 1 THEN 1 END) as decimal(5,2))) As c3
        FROM   @MyTable AS td

    FETCH NEXT FROM mycur INTO @Min, @Max, @Desc
END 

CLOSE mycur
DEALLOCATE mycur

SELECT * FROM @Result

问题是我要删除CURSOR并在没有它的情况下编写查询。在这种情况下它怎么可能?

5 个答案:

答案 0 :(得分:5)

尝试使用CROSS JOIN并过滤where子句或条件CASE语句。目前还不清楚你的最终目标是什么,所以下面的总量必须调整,但这可能是一个好的开始:

SELECT 
  mm.[Desc],
  (CAST(SUM(CASE WHEN MyCol1 >= mm.Min AND MyCol1 <= mm.Max THEN 1 ELSE 0 END) AS DECIMAL(5,2)) / CAST(COUNT(Id) AS DECIMAL(5,2))) AS C1,
  (CAST(SUM(CASE WHEN MyCol2 = 1 AND MyCol3 IS NOT NULL THEN 1 END) AS DECIMAL(5,2)) / CAST(SUM(CASE WHEN MyCol1 >= mm.Min AND MyCol1 <= mm.Max THEN 1 ELSE 0 END) AS DECIMAL(5,2))) AS C2,
  (CAST(SUM(CASE WHEN MyCol1 >= mm.Min AND MyCol1 <= mm.Max AND MyCol2 = 1 THEN 1 ELSE 0 END) AS DECIMAL(5,2)) / CAST(SUM(CASE WHEN MyCol2 = 1 THEN 1 ELSE 0 END) AS DECIMAL(5,2))) AS C3
FROM MyTable td
  CROSS JOIN MyMinMaxTable mm
GROUP BY mm.[Desc]

答案 1 :(得分:2)

这是一个解决方案。我解开你的查询并重新编写它。 如果你添加额外的范围,并且不需要CROSS APPLY,硬编码的子查询范围或其他任何东西,这个继续工作。

排序是一项练习: - )

Declare @MyMinMaxTable Table
(
    [Min]    int,
    [Max]    int,
    [Desc]   NVARCHAR(50)
)

Insert into @MyMinMaxTable
values (0,12,N'Child'),
       (13,19,N'Teenager'),
       (20,25,N'Youth'),
       (25,40,N'Middle-aged'),
       (40,99,N'Old')

Declare @MyTable Table
(
    Id         int identity(1,1),
    [Year]     int,
    MyCol1     int,
    MyCol2     int,
    MyCol3     int null
)

Insert into @MyTable
([Year], MyCol1, MyCol2, MyCol3)
values 
(2012, 10, 1 , 1),
(2012, 28, 2 , 3),
(2012, 14, 1 , 7),
(2012, 24, 3 , 3),
(2012, 80, 1 , 6),
(2012, 39, 1 , 3),
(2012, 45, 1 , 5),
(2012, 23, 2 , 6),
(2012, 72, 3 , 8),
(2012, 17, 1 , null),
(2012, 62, 4 , 9),
(2012, 20, 1 , null),
(2012, 5, 1 , 9),
(2012, 8, 1 , 9),
(2012, 25, 1 , null),
(2012, 41, 2 , 2),
(2012, 26, 1 , 2),
(2012, 33, 4 , 2),
(2012, 40, 1 , 2),
(2012, 33, 2 , 3),
(2012, 41, 1 , 5),
(2012, 53, 1 , null),
(2012, 37, 1 , 3)

SELECT
    MMT.[Desc]
    --Ratio of (Age Bracket Matches) / (ALL Rows)
    , CAST(COUNT(1) / (SELECT CAST(COUNT(1) AS DECIMAL(15,2)) AS TotalRows FROM @MyTable) AS DECIMAL(15,2)) AS C1
    --Ratio of (Rows WHERE MyCol2 = 1 and MyCol3 = NULL) / (Age Bracket Matches)
    , CAST((SELECT COUNT(1) FROM @MyTable WHERE MyCol2 = 1 AND MyCol3 IS NOT NULL) / CAST(COUNT(1) AS DECIMAL(15,2)) AS DECIMAL(15,2)) AS C2
    --Ratio of (Age Bracket Matches WHERE MyCol2 = 1) / (Rows WHERE WHERE MyCol2 = 1)
    , CAST(SUM(CASE WHEN T.MyCol2 = 1 THEN 1 ELSE 0 END) / (SELECT CAST(COUNT(1) AS DECIMAL(15,2)) FROM @MyTable WHERE MyCol2 = 1) AS DECIMAL(15,2)) AS C3
FROM
    @MyMinMaxTable AS MMT
    INNER JOIN @MyTable AS T ON
        T.MyCol1 BETWEEN MMT.[Min] AND MMT.[Max]
GROUP BY
    MMT.[Desc]

答案 2 :(得分:0)

好像你试图将数据分成几组。试试这个查询。但我不确定第三栏,特别是没有样本数据

{{1}}

答案 3 :(得分:0)

另一种方法可以是使用CROSS APPLY

   SELECT MM.[Desc], 
       CAST(C1.C1Count AS DECIMAL(15, 2)) / CAST(Tot.TotCount AS decimal (15, 2))  AS C1, 
       CAST(C2.C2Count AS DECIMAL(15, 2)) / CAST(C1.C1Count AS decimal (15, 2))    AS C2, 
       CAST(C3.C3Count AS DECIMAL(15, 2)) / CAST(C4.C4Count AS decimal (15, 2))    AS C2 
   FROM   @MyMinMaxTable MM 
       CROSS APPLY (SELECT COUNT(MyCol1) AS C1Count 
                    FROM   @MyTable 
                    WHERE  ( MyCol1 BETWEEN MM.Min AND MM.Max )) C1 
       CROSS APPLY (SELECT COUNT(*) C2Count 
                    FROM   @MyTable 
                    WHERE  MyCol2 = 1 
                           AND MyCol3 IS NOT NULL)C2 
       CROSS APPLY (SELECT COUNT(*) C3Count 
                    FROM   @MyTable 
                    WHERE  MyCol1 >= mm.Min 
                           AND MyCol1 <= mm.Max 
                           AND MyCol2 = 1)C3 
       CROSS APPLY (SELECT COUNT(*) C4Count 
                    FROM   @MyTable 
                    WHERE  MyCol2 = 1)C4 
       CROSS APPLY (SELECT COUNT(*) TotCount 
                    FROM   @MyTable)Tot 

以下是上述查询的输出。

Desc                        C1                      C2                  C2
----------          --------------------    ---------------------   ---------------------
Child               0.130434782608695652    3.666666666666666666    0.200000000000000000
Teenager            0.086956521739130434    5.500000000000000000    0.133333333333333333
Youth               0.173913043478260869    2.750000000000000000    0.133333333333333333
Middle-aged         0.347826086956521739    1.375000000000000000    0.333333333333333333
Old                 0.347826086956521739    1.375000000000000000    0.333333333333333333

答案 4 :(得分:0)

我认为在这种情况下可以预先计算&#34;计算@ MyTable&#34;中的所有记录,“@ MyTol2中的记录计数,其中MyCol2 = 1且MyCol3不为空”和“计数@MyTable中的记录,其中MyCol2 = 1“并将它们存储到变量中。

这将使脚本更易读,更容易理解。

在这里。希望这会有所帮助。

Declare @MyMinMaxTable Table
(
    [Min]    int,
    [Max]    int,
    [Desc]   NVARCHAR(50)
)

Insert into @MyMinMaxTable
values (0,12,N'Child'),
       (13,19,N'Teenager'),
       (20,25,N'Youth'),
       (25,40,N'Middle-aged'),
       (40,99,N'Old')

Declare @MyTable Table
(
    Id         int identity(1,1),
    [Year]     int,
    Age        int,
    MyCol2     int,
    MyCol3     int null
)

Insert into @MyTable
([Year], Age, MyCol2, MyCol3)
values 
(2012, 10, 1 , 1),
(2012, 28, 2 , 3),
(2012, 14, 1 , 7),
(2012, 24, 3 , 3),
(2012, 80, 1 , 6),
(2012, 39, 1 , 3),
(2012, 45, 1 , 5),
(2012, 23, 2 , 6),
(2012, 72, 3 , 8),
(2012, 17, 1 , null),
(2012, 62, 4 , 9),
(2012, 20, 1 , null),
(2012, 5, 1 , 9),
(2012, 8, 1 , 9),
(2012, 25, 1 , null),
(2012, 41, 2 , 2),
(2012, 26, 1 , 2),
(2012, 33, 4 , 2),
(2012, 40, 1 , 2),
(2012, 33, 2 , 3),
(2012, 41, 1 , 5),
(2012, 53, 1 , null),
(2012, 37, 1 , 3)

Declare @Result Table
(
    C0         NVARCHAR(50),
    c1         decimal(5,2),
    C2         decimal(5,2),
    C3         decimal(5,2)
)


-- ANSWER BEGIN HERE

-- The count all record in @MyTable
DECLARE @CountAll DECIMAL(5,2)
SET @CountAll = (SELECT COUNT(1) FROM @MyTable)

-- The count record in @MyTable where MyCol2 = 1 and MyCol3 is not null
DECLARE @CountMyCol2Eq1AndMyCol3NotNull DECIMAL(5,2)
SET @CountMyCol2Eq1AndMyCol3NotNull = (SELECT COUNT(1) FROM @MyTable WHERE MyCol2 = 1 AND MyCol3 IS NOT NULL)

-- The count record in @MyTable where MyCol2 = 1
DECLARE @CountMyCol2Eq1 DECIMAL(5,2)
SET @CountMyCol2Eq1 = (SELECT COUNT(1) FROM @MyTable WHERE MyCol2 = 1)

INSERT INTO @Result
SELECT mm.[Desc],
    COUNT(1) / @CountAll,
    @CountMyCol2Eq1AndMyCol3NotNull / COUNT(1),
    SUM(CASE WHEN tt.MyCol2 = 1 THEN 1 ELSE 0 END) / @CountMyCol2Eq1
FROM @MyMinMAxTable mm
JOIN @MyTable tt
    ON mm.[Min] <= tt.Age
    AND mm.[Max] >= tt.Age
GROUP BY mm.[Desc]

SELECT * FROM @Result