【问题标题】:T-SQL SELECT with multiple variables具有多个变量的 T-SQL SELECT
【发布时间】:2019-05-24 06:38:54
【问题描述】:

我正在尝试使用 SQL 语句和 NORTHWIND 数据库获取此输出:

Employee Name:Nancy Davolio
Number of Sales:345
Total Sales:192107.60

Employee Name:Andrew Fuller
Number of Sales:241
Total Sales:166537.75

Employee Name:Janet Leverling
Number of Sales:321
Total Sales:202812.84

Employee Name:Margaret Peacock
Number of Sales:420
Total Sales:232890.85

Employee Name:Steven Buchanan
Number of Sales:117
Total Sales:68792.28

...还有 4 个条目

当我使用这个语句时:

USE Northwind

DECLARE @EmployeeName VARCHAR(40),
        @NumberOfSales INT,
        @TotalSales DECIMAL(10,2),
        @Counter TINYINT = 1,
        @NumEmployees INT = IDENT_CURRENT('dbo.Employees');

WHILE @Counter < @NumEmployees
BEGIN
    --SELECT @EmployeeName = E.FirstName+' '+E.LastName
    --SELECT @NumberOfSales = count(od.OrderID)
    SELECT @TotalSales = SUM(unitprice * quantity * (1 - Discount))
    FROM Employees E
    JOIN Orders AS O ON O.EmployeeID = E.EmployeeID
    JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID
    WHERE E.EmployeeID = @Counter

    PRINT 'Employee Name:       '--+ @EmployeeName;
    PRINT 'Number of Sales:     '--+ LTRIM(STR(@NumberOfSales));
    PRINT 'Total Sales:         '+CONVERT(varchar(10),@TotalSales);
    PRINT '';

    SET @Counter += 1;
END

我可以让每个选择单独工作,但我无法弄清楚让单个 SELECT 语句完成所有工作的语法。我也应该能够用三个SET 语句来做到这一点,但我也无法弄清楚。指向这两种可能性的指针会很棒。

这是实际的步骤用语: “在循环中,使用 SELECT 语句检索每个员工的名字和姓氏、每个员工处理的订单数量以及每个员工的总销售额(您正在逐个处理每个员工)。您将需要将多个表连接在一起并使用聚合函数来获得计数和总数。将串联的全名、销售额和总销售额分配给适当的变量。"

输出应该在消息选项卡中,除了上面列出的预期输出之外,没有表格或格式。

【问题讨论】:

  • “输出应在消息选项卡中”表示使用PrintRaIsErrorThrow 输出数据。 AFAIK,查询结果无法重定向到“消息”选项卡。您可以编写一个查询,例如就像那些用于构建逗号分隔列表的方法一样,生成包含所有数据(包括换行符)的单个字符串,然后 Print 生成该结果。这不是通常编写应用程序的方式。

标签: sql sql-server tsql select while-loop


【解决方案1】:

不需要循环(如果可能,应该避免RBAR - Row By Agonizing Row方法):

SELECT EmployeeID
       ,[Employee Name] = E.FirstName+' '+E.LastName
       ,[TotalSales] = SUM(unitprice * quantity * (1-Discount))
       ,[NumberOfSales] = COUNT(DISTINCT o.OrderID)
FROM Employees E
JOIN Orders AS O ON O.EmployeeID = E.EmployeeID
JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID
GROUP BY E.EmployeeID, E.FirstName+' '+E.LastName
ORDER BY E.EmployeeID;

编辑:

循环版本 - 一次分配多个变量。

USE Northwind

DECLARE @EmployeeName VARCHAR(40),
        @NumberOfSales INT,
        @TotalSales DECIMAL(10,2),
        @Counter TINYINT = 1,
        @NumEmployees INT = IDENT_CURRENT('dbo.Employees');

WHILE @Counter < @NumEmployees
BEGIN
    SELECT @EmployeeName  = E.FirstName+' '+E.LastName
          ,@NumberOfSales = COUNT(DISTINCT o.OrderID)
          ,@TotalSales    = SUM(unitprice * quantity * (1 - Discount))
    FROM Employees E
    JOIN Orders AS O ON O.EmployeeID = E.EmployeeID
    JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID
    WHERE E.EmployeeID = @Counter
    GROUP BY E.FirstName+' '+E.LastName;

    PRINT 'Employee Name:       '+ @EmployeeName;
    PRINT 'Number of Sales:     '+ LTRIM(STR(@NumberOfSales));
    PRINT 'Total Sales:         '+ CONVERT(varchar(10),@TotalSales);
    PRINT '';

    SET @Counter += 1;
END

请注意,当您有间隙时,使用WHILE 循环可能效率很低(即您从 1 开始到 IDENT_CURRENT,这可能是您的 id 像 1,5, 200671 这样的情况,而您最终有不必要的循环)。


编辑 2:

在选择中发生多个分配时,似乎需要 GROUP BY

我添加了GROUP BY,因为 FirstName 和 LastName 没有被聚合函数包装。您可以跳过该子句,但您需要添加 MIN/MAX 函数:

SELECT @EmployeeName  = MIN(E.FirstName)+' '+MIN(E.LastName)
      ,@NumberOfSales = COUNT(DISTINCT o.OrderID)
      ,@TotalSales    = SUM(unitprice * quantity * (1 - Discount))
FROM Employees E
JOIN Orders AS O ON O.EmployeeID = E.EmployeeID
JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID
WHERE E.EmployeeID = @Counter;
-- and we are sure that all values for First/Last nane are the same because of  
-- WHERE E.EmployeeID = @Counter

相关:Group by clause

在标准 SQL 中,包含 GROUP BY 子句的查询不能引用选择列表中未在 GROUP BY 子句中命名的非聚合列

【讨论】:

  • 因为,把我放在飞机上,我们都会死。教我爬、走、跑、跳……然后我就可以飞了。虽然我大部分时间(因为我想学习但并不完全理解你的陈述)欣赏你的代码的简洁性,但我仍然需要学习这些不可或缺的痛苦掘金。
  • @TimGibney 如果你真的必须使用LOOP,那么你可以一次分配多个变量。
  • 是的,完美的@Lukasz Szozda!谢谢你。当多个分配在“选择” SPAN>中发生多个分配时,似乎是必需的
  • @TimGibney 我已经为那部分添加了说明
  • @TimGibney 另一个选项是 CURSOR LOOP,但我认为它现在超出了你的练习范围
【解决方案2】:

应该这样做。我使用 CROSS APPLY 来取消旋转集合,然后相应地对其进行格式化。您可以在名为“CROSS APPLY an Alternative Method to Unpivot”的文章中阅读有关它的更多信息。由于 SQL 与集合一起工作,因此在我看来,SQL 的输入和输出应该始终是集合。

恐怕您格式化的方式可能不是 SQL 的工作,但仍然可以使用“单个”选择语句作为集合操作:

;WITH CTE AS 
(
SELECT 
        EMPLOYEENAME =    E.FirstName +' '+ E.LastName,  
        NUMBEROFORDERS =  COUNT(OD.OrderID),
        TOTALSALES =      SUM(unitprice * quantity * (1-Discount))

   FROM Employees E
   INNER JOIN Orders AS O ON O.EmployeeID = E.EmployeeID
   INNER JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID
   GROUP BY E.FirstName + ' ' + E.LastName
)


SELECT COLNAME, ColValue
FROM CTE

CROSS APPLY ( VALUES ('Employe Name:', EMPLOYEENAME),
                     ('Number of Sales:', LTRIM(STR(NUMBEROFORDERS, 25, 5)) ),
                     ('Total Sales:', LTRIM(STR(TOTALSALES, 25, 5)) ),
                     ('','')

            ) A  (COLNAME, ColValue)

示例输出如下:

COLNAME             ColValue
-------------   | ------------- 
Employe Name:   | Nancy Davolio
Number of Sales:|  345.00000
Total Sales:    |  192107.60432

【讨论】:

  • 所以我列出的预期输出来自我正在获得的二手正式“学习 SQL”课程。该工作簿陈述了明确的说明,但除了预期的输出之外,提供的“答案”非常少。所以严格来说,列出的输出与预期不符。此外,严格来说,CROSS APPLY 不在本节中,“CTE”也不在此部分中。
  • 嗯,这很有趣,如果您碰巧想通了或偶然发现了解决方案,请在此处发布。我只是好奇:)
猜你喜欢
  • 1970-01-01
  • 2011-04-19
  • 2011-10-18
  • 1970-01-01
  • 2016-06-11
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 2020-11-28
相关资源
最近更新 更多