我有一个我认为是相当复杂的查询(至少对我来说),因此我决定尝试使用动态SQL解决它。但是,我有两个无法解决的问题。
情况
在表中,用户可以输入具有金额,星期和状态的项目。 因此,数据应该类似于这种格式。
Week 1 Week 2
Status 1 | 50 25
Status 2 10 20
这是SQL中的数据。
Status 1 | Week 1 |25
Status 1 | Week 1 |25
Status 1 | Week 2 |25
Status 2 | Week 1 | 2
Status 2 | Week 1 | 8
Status 2 | Week 1 | 10
Status 2 | Week 1 | 10
对于每个状态,我都使用动态数据透视表以周为单位汇总金额。
我尝试了什么
--EXEC usp_weekReport @weeks=1, @year='2019'
ALTER PROC usp_weekReport
(
@weeks INT,
@year NVARCHAR(4)
)
AS
DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns += N', p.' + QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR,P.date)=@year
AND
([week] IN (@weeks)
OR
[week] IN (@weeks+1)
OR
[week] IN (@weeks+2)
OR
[week] IN (@weeks+3)
OR
[week] IN (@weeks+4)
OR
[week] IN (@weeks+5)
)
GROUP BY P.[week]
) AS x;
SET @sql = N'
SELECT p.[statusName],' + STUFF(@columns, 1, 2, '') + '
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('
+ STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
--PRINT @sql;
--EXEC sp_executesql @sql;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0
--weekC int DEFAULT 0,
--weekD int DEFAULT 0,
--weekE int DEFAULT 0,
--weekF int DEFAULT 0
)
INSERT #reportResult Exec(@sql)
SELECT statusName, weekA,weekB--,weekC,weekD,weekE,weekF -- here you have "static SELECT with field names"
FROM #reportResult
DROP TABLE #reportResult
为解决这个问题,我有上面的代码,当它工作时(返回SQL中的值),我有两个问题。
问题 我的第一个问题是,我不能将此代码与在Crystal报表中创建报表一起使用。当我导入存储过程时,这些列显示为空白。我看了下面的链接。 [从动态SQL查询中选择字段名称] [1]
[1]:SELECT fieldnames FROM dynamic SQL query,即使我尝试按照据说确实有效的方式为答案建模。它对我似乎不起作用,因为我的数据列在Crystal中仍然为空白。 我本来想从一个单独的存储过程中调用我的第一个存储过程,但是考虑到我的答案是如何返回的(不仅仅是我可以分配给变量的单个值,目前正在考虑返回一个表值函数)我对此表示怀疑会起作用。
出现的第二个问题是因为我的“周”是动态的(长达6周),因此无法创建带有“备用”列的临时表,或者出现错误(列数不正确),如您所见,他们出来,我也不能使用“选择进入”
感谢提供的任何帮助或想法。
答案 0 :(得分:0)
尝试以下操作,它使用全局TEMP表:
ALTER PROCEDURE usp_weekReport
@weeks INT,
@year NVARCHAR(4)
AS
BEGIN
DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = @year
AND ([week] IN(@weeks)
OR [week] IN(@weeks + 1)
OR [week] IN(@weeks + 2)
OR [week] IN(@weeks + 3)
OR [week] IN(@weeks + 4)
OR [week] IN(@weeks + 5))
GROUP BY P.[week]
) AS x;
SET @sql = N'
SELECT p.[statusName],'+STUFF(@columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql @sql;
SELECT *
FROM ##reportResult;
IF OBJECT_ID('tempdb..##reportResult') IS NOT NULL
BEGIN
DROP TABLE ##reportResult;
END;
END
这是没有全局临时表的解决方案:
ALTER PROCEDURE usp_weekReport
@weeks INT,
@year NVARCHAR(4)
AS
BEGIN
DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX), @COLCOUNT int, @TableColumns NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = @year
AND ([week] IN(@weeks)
OR [week] IN(@weeks + 1)
OR [week] IN(@weeks + 2)
OR [week] IN(@weeks + 3)
OR [week] IN(@weeks + 4)
OR [week] IN(@weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT @COLCOUNT = count(*)
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = @year
AND ([week] IN(@weeks)
OR [week] IN(@weeks + 1)
OR [week] IN(@weeks + 2)
OR [week] IN(@weeks + 3)
OR [week] IN(@weeks + 4)
OR [week] IN(@weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT @TableColumns = CASE When @COLCOUNT = 1 THEN 'weekA'
When @COLCOUNT = 2 THEN 'weekA, weekB'
When @COLCOUNT = 3 THEN 'weekA, weekB, weekC'
When @COLCOUNT = 4 THEN 'weekA, weekB, weekC, weekD'
When @COLCOUNT = 5 THEN 'weekA, weekB, weekC, weekD, weekE'
When @COLCOUNT = 6 THEN 'weekA, weekB, weekC, weekD, weekE, weekF'
end;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0,
weekC int DEFAULT 0,
weekD int DEFAULT 0,
weekE int DEFAULT 0,
weekF int DEFAULT 0
)
SET @sql = N'
INSERT INTO #reportResult (statusName,' + @TableColumns + ')
SELECT p.[statusName],'+STUFF(@columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql @sql;
SELECT *
FROM #reportResult;
IF OBJECT_ID('tempdb..#reportResult') IS NOT NULL
BEGIN
DROP TABLE #reportResult;
END;
END