简体   繁体   English

将Access Crosstab / PIVOT查询转换为T-SQL

[英]Convert Access Crosstab/PIVOT query to T-SQL

Access Query that I need to convert to work under SQL Server: 我需要转换为在SQL Server下工作的Access Query:

TRANSFORM Sum([T_Leads]![OrderType]='New Order')-1 & " / " & Sum([T_Leads]![OrderType]='Change Order')-1 
AS [New / Change]   
SELECT Employees.EmployeeName as Name, Count(T_Leads.OrderType) AS Total
FROM Employees INNER JOIN T_Leads ON Employees.EmployeeID = T_Leads.EmployeeID
WHERE (((T_Leads.Date)>Date()-7))
and [Employees.LeadRotation] <> "Inactive"
GROUP BY Employees.EmployeeName
ORDER BY T_Leads.Date
PIVOT T_Leads.Date;

The output displays a list of employees currently taking leads (who are not "inactive"). 输出显示当前正在接受潜在客户(不是“非活动”)的员工列表。 For the column headers, the date is shown for the previous seven days (if a lead was submitted on that day), and two totals are displayed under each date. 对于列标题,显示前七天的日期(如果当天提交了潜在客户),并在每个日期下显示两个总计。 One for the total number of New Orders received, and another for total number of Change Orders. 一个是收到的新订单总数,另一个是变更订单总数。 I've not been able to find any examples that generate the date columns and display two values under each column. 我无法找到任何生成日期列的示例,并在每列下显示两个值。

The Access query currently produces output like this in a GridView: Access查询当前在GridView中生成如下输出:

+-------------+-------+----------+----------+----------+----------+-----------+
| Name        | Total | 4/5/2016 | 4/6/2016 | 4/7/2016 | 4/8/2016 | 4/11/2016 |  
+-------------+-------+----------+----------+----------+----------+-----------+
| Doe, Jane   |  9    | 0/1      | 0/2      | 0/3      | /        | 0/3       |
+-------------+-------+----------+----------+----------+----------+-----------+
| Guy, Some   | 4     | 0/1      | 0/1      | /        | /        | 0/2       | 
+-------------+-------+----------+----------+----------+----------+-----------+
| Doe, John   | 10    | 0/1      | 1/1      | 2/1      | 0/3      | 0/1       |

Sample Data: 样本数据:

| EmployeeID  | Customer         | Date       | OrderType|
+-------------+------------------+------------+----------+
| 1           | Fake Customer    | 2016-05-14 | New      | 
+-------------+------------------+------------+----------+
| 2           | Some Company     | 2016-05-13 | Change   | 
+-------------+------------------+------------+----------+
| 3           | Stuff Inc.       | 2016-05-14 | New      | 
+-------------+------------------+------------+----------+
| 3           | Cool Things      | 2016-05-12 | Change   |

You'd need to dynamically produce your pivot columns then do case statements for each of them. 您需要动态生成透视列,然后为每个透明列执行case语句。 The following is an example of how you could do it: 以下是如何执行此操作的示例:

IF OBJECT_ID('tmpEmployees_Test', 'U') IS NOT NULL DROP TABLE tmpEmployees_Test;
CREATE TABLE tmpEmployees_Test (EmployeeID INT, EmployeeName VARCHAR(255));
INSERT tmpEmployees_Test (EmployeeID, EmployeeName)
VALUES (1, 'Doe, Jane'), (2, 'Doe, John'), (3, 'Guy, Some');

IF OBJECT_ID('tmpOrders_Test', 'U') IS NOT NULL DROP TABLE tmpOrders_Test;
CREATE TABLE tmpOrders_Test (EmployeeID INT, Customer VARCHAR(255), Date DATE, OrderType VARCHAR(255));
INSERT tmpOrders_Test (EmployeeID, Customer, Date, OrderType)
VALUES (1, 'Fake Customer', '2016-05-14', 'New')
        , (2, 'Some Company', '2016-05-13', 'Change')
        , (3, 'Stuff Inc.', '2016-05-14', 'New')
        , (3, 'Cool Things', '2016-05-12', 'Change')
        , (3, 'Amazing Things', '2016-05-12', 'Change');

DECLARE @startDate DATE = '2016-05-14', @cols VARCHAR(MAX) = '', @cols2 VARCHAR(MAX) = '';

SELECT @cols += ', CONVERT(VARCHAR(255), SUM(CASE WHEN O.Date = ''' + CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate)) + ''' AND O.OrderType = ''New'' THEN 1 ELSE 0 END)) + ''/'' + CONVERT(VARCHAR(255), SUM(CASE WHEN O.Date = ''' + CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate)) + ''' AND O.OrderType = ''Change'' THEN 1 ELSE 0 END)) ' + QUOTENAME(CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate), 103)) + CHAR(10) + CHAR(9) + CHAR(9)
        , @cols2 += ', CASE WHEN ' + QUOTENAME(CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate), 103)) + ' = ''0/0'' THEN ''/'' ELSE ' + QUOTENAME(CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate), 103))  + ' END ' + QUOTENAME(CONVERT(VARCHAR(255), DATEADD(DD, X.Y, @startDate), 103)) + CHAR(10) + CHAR(9)
FROM (VALUES (0),(-1),(-2),(-3),(-4),(-5),(-6)) X(Y)
JOIN tmpOrders_Test O ON O.Date = DATEADD(DD, X.Y, @startDate)
GROUP BY X.Y
ORDER BY X.Y;

DECLARE @SQL VARCHAR(MAX) = '
WITH T AS (
    SELECT E.EmployeeID
        , COUNT(*) Total
        ' + @cols + '
    FROM tmpEmployees_Test E
    JOIN tmpOrders_Test O ON O.EmployeeID = E.EmployeeID
    WHERE O.Date BETWEEN ''' + CONVERT(VARCHAR(255), DATEADD(dd, -6, @startDate)) + ''' AND ''' + CONVERT(VARCHAR(255), @startDate) + '''
    GROUP BY E.EmployeeID)
SELECT E.EmployeeName
    , Total
    ' + @cols2 + '
FROM T
JOIN tmpEmployees_Test E ON E.EmployeeID = T.EmployeeID;'

--PRINT @SQL;
EXEC(@SQL);

This mirrors the output you're expecting (as far as I can tell), even if it looks a bit messy. 这反映了你期望的输出(据我所知),即使它看起来有点乱。 I don't think you can produce your desired output without it being a bit messy, though. 但是,如果没有它有点混乱,我认为你不能产生你想要的输出。

Note: The CTE in the dynamic SQL is just to get rid of all the '0/0' and make them '/' and it seems the easiest way to do it. 注意:动态SQL中的CTE只是去除所有'0/0'并使它们'/',这似乎是最简单的方法。

Currently, your crosstab is not straightforward for easy pivot translation as you have two conditional aggregates which are then concatenated into a string value. 目前,您的交叉表不易直接进行数据透视转换,因为您有两个条件聚合,然后连接成字符串值。 Plus, you have an aggregate group count. 另外,您有一个聚合小组计数。

In TSQL, this query will be a little intricate. 在TSQL中,此查询将有点复杂。 Consider the below approach using two derived table pivot aggregate queries joined together with use of CAST . 考虑以下方法,使用两个使用CAST连接在一起的派生表数据集聚合查询。

Source (temp table -thanks to @ZLK) 来源 (临时表 - 谢谢@ZLK)

CREATE TABLE #tmpOrders (EmployeeID INT, Customer VARCHAR(255), 
                         Date DATE, OrderType VARCHAR(255));
INSERT #tmpOrders (EmployeeID, Customer, Date, OrderType)
VALUES (1, 'Fake Customer', '2016-05-14', 'New'),
       (2, 'Some Company', '2016-05-13', 'Change'),
       (3, 'Stuff Inc.', '2016-05-14', 'New'),
       (3, 'Cool Things', '2016-05-12', 'Change'),
       (3, 'Amazing Things', '2016-05-12', 'Change');

Query (inner joins of two pivots) 查询 (两个枢轴的内部连接)

SELECT pvt1.Name, pvt1.Total,
       CAST(pvt1.[2016-05-12] AS VARCHAR(2)) + '/' 
            +  CAST(pvt2.[2016-05-12] AS VARCHAR(2)) As [05/12/2016], 
       CAST(pvt1.[2016-05-13] AS VARCHAR(2)) + '/' 
            +  CAST(pvt2.[2016-05-13] AS VARCHAR(2)) As [05/13/2016], 
       CAST(pvt1.[2016-05-14] AS VARCHAR(2)) + '/' 
            +  CAST(pvt2.[2016-05-14] AS VARCHAR(2)) As [05/14/2016]        
FROM     
    (SELECT t.Customer as Name, t.[Date],
            COUNT(*) As Total, 
            SUM(CASE WHEN t.OrderType = 'New' THEN 1 ELSE NULL END) AS NewOrders

      FROM #tmpOrders t
      WHERE (t.[Date] > GETDATE()-7)    
      GROUP BY t.Customer, t.[Date]) AS src1     
      PIVOT 
          (
            COUNT(src1.[NewOrders]) 
            FOR src1.[Date] IN ([2016-05-12], [2016-05-13], [2016-05-14])
          ) AS pvt1

INNER JOIN 
    (SELECT t.Customer as Name, t.[Date],
            COUNT(*) As Total, 
            SUM(CASE WHEN t.OrderType = 'Change' THEN 1 ELSE NULL END) AS ChangeOrders
      FROM #tmpOrders t
      WHERE (t.[Date] > GETDATE()-7)    
      GROUP BY t.Customer, t.[Date]) AS src1     
      PIVOT 
          (
            COUNT(src1.[ChangeOrders]) 
            FOR src1.[Date] IN ([2016-05-12], [2016-05-13], [2016-05-14])
          ) AS pvt2        
ON pvt1.Name = pvt2.Name

Output 产量

--Name          Total   05/12/2016  05/13/2016  05/14/2016
--Amazing Things    1         0/1          0/0         0/0
--Cool Things       1         0/1          0/0         0/0
--Fake Customer     1         0/0          0/0         1/0
--Some Company      1         0/0          0/1         0/0
--Stuff Inc.        1         0/0          0/0         1/0

Do note: one thing you lose in translation is the dynamic quality of Access' crosstab query which will output all existing values (up until the 255 column limit). 请注意:您在翻译中丢失的一件事是Access'交叉表查询的动态质量,它将输出所有现有值(直到255列限制)。 In SQL Server, for regular pivots you need to declare such values in advance or use stored procs/functions to return all values dynamically. 在SQL Server中,对于常规枢轴,您需要提前声明此类值或使用存储过程/函数动态返回所有值。

IF OBJECT_ID('tmpEmployees_Test', 'U') IS NOT NULL DROP TABLE tmpEmployees_Test;
CREATE TABLE tmpEmployees_Test (EmployeeID INT, EmployeeName VARCHAR(255));
INSERT tmpEmployees_Test (EmployeeID, EmployeeName)
VALUES (1, 'Doe, Jane'), (2, 'Doe, John'), (3, 'Guy, Some');


IF OBJECT_ID('tmpOrders_Test', 'U') IS NOT NULL DROP TABLE tmpOrders_Test;
CREATE TABLE tmpOrders_Test (EmployeeID INT, Customer VARCHAR(255), Date DATE, OrderType VARCHAR(255));
INSERT tmpOrders_Test (EmployeeID, Customer, Date, OrderType)
VALUES (1, 'Fake Customer', '2016-05-14', 'New')
        , (2, 'Some Company', '2016-05-13', 'Change')
        , (3, 'Stuff Inc.', '2016-05-14', 'New')
        , (3, 'Cool Things', '2016-05-12', 'Change')
        , (3, 'Amazing Things', '2016-05-12', 'Change');


DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns += N', p.' + QUOTENAME(Name) 
  FROM (SELECT distinct CONVERT(nvarchar(30) , p.Date , 101) as Name FROM dbo.tmpOrders_Test AS p where [Date] > GETDATE()-7
  ) AS x;
-- Kept it for formatting Purpose
DECLARE @columns1 NVARCHAR(MAX)
SET @columns1 = N'';
SELECT @columns1 += N', ISNULL(p.' + QUOTENAME(Name) + ',''/'') AS ' + QUOTENAME(Name) 
  FROM (SELECT distinct CONVERT(nvarchar(30) , p.Date , 101) as Name FROM dbo.tmpOrders_Test AS p where [Date] > GETDATE()-7
  ) AS x;


SET @sql = N'
SELECT EmployeeName, Count(*) as Total  ' +  @columns1  + '
FROM
(
    SELECT  EmployeeID, EmployeeName' + ''+ @columns1 + '' + '
    FROM
    (
      SELECT    o.employeeID,EmployeeName, CAST(COUNT(case WHEN OrderType = ''New'' then 1  end) as varchar(5)) + ''/'' + 
                CAST(COUNT(case WHEN OrderType = ''Change'' then 1  end) as varchar(5)) as OrderType, CONVERT(nvarchar(30) , p.Date , 101) as Date
       FROM     dbo.tmpOrders_Test AS p
                    INNER JOIN dbo.tmpEmployees_Test AS o
                     ON p.EmployeeID = o.EmployeeID
       GROUP BY EmployeeName, Date, o.employeeID
    ) AS j
    PIVOT
    (
      Max(OrderType) FOR Date IN ('
      + STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')
      + ')
    ) AS p) as p JOIN tmpOrders_Test as m on p.employeeID = m.employeeID
where [Date] > GETDATE()-7
GROUP BY EmployeeName ' + @columns + '
';

PRINT @sql;
EXEC sp_executesql @sql;

This one is using dynamic Pivot. 这个是使用动态Pivot。 You might want to do this business logic on Application or Reporting Side instead of complex sql. 您可能希望在Application或Reporting Side上执行此业务逻辑,而不是复杂的sql。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM