SQL view好像没有完全从原来的数据中抽象出来?
SQL view seems to not completely abstract data from the original?
创建了一个视图来提取数据并在多个报告中使用,vwRPWIP_POST。
SELECT TOP (100) PERCENT
Job, MDescription, MWarehouse, MReference, TrnDate
FROM
CompanyE.dbo.WipJobPost AS wjp
WHERE
(Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (LEFT(MReference, 1) LIKE '[0-9]')
它returns预期数据:
问题是当人们在 MReference
列中输入字母字符时,特别是前三个字符,SQL returns 将这些字母字符与 Julian 日期进行比较时出现错误(例如 203)。
MReference
通常包含 Julian 日期和另一个字母指示符,通常看起来像 203-MU,但将继续被允许保留违规数据。当存在违规数据时,这些记录将直接从报告中删除。
使用 T-SQL LEFT
,我将 203 与今天的 Julian 日期进行比较,进行计算并根据该结果构建适当记录的报告。有时 MReference
包含“DUMMY”、“SPACERS”、“RED-MU”等字符串。
因此,我使用 WHERE
子句中的最后一个 AND 子句对 vwRPWIP_POST
进行了修改,仅包含 MReference
中第一列为数字的记录。这行得通,当我编辑 SSMS 中的所有行时,只能看到至少第一个字符是数字的记录。
存储过程在这里:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp12DAYRESINREPORT]
@parFromDate NVARCHAR(5)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @FromDate NVARCHAR(5),@Locn NVARCHAR(10) = 'PRES';
SET @FromDate = @parFromDate;
SELECT DISTINCT
A.Job, A.JobDescription, A.QtyToMake, A.JobDeliveryDate,
A.MDescription, A.MWarehouse, A.MReference, A.HierHead1,
A.OperCompleted, A.WorkCentre,
B.LocnSummed QtyCompleted
FROM
(SELECT
wm.Job, wm.JobDescription, wm.JobDeliveryDate, wm.QtyToMake,
wp.MDescription, wp.MWarehouse, wp.MReference, wal.HierHead1
, wal.OperCompleted, wal.QtyCompleted, wal.WorkCentre
, LEFT(wm.JOB,7) AS MID7
FROM
TablesCoE.dbo.vwRPWIP_POST wp
INNER JOIN
(TablesCoE.dbo.vwRPWIP_ALLLAB wal
INNER JOIN
TablesCoE.dbo.vwRPWIP_Master wm
ON
wal.Job = wm.Job)
ON
wp.Job = wm.Job
WHERE wp.MWarehouse = 'F'
AND LEFT(wp.MReference,3) <= @FromDate
AND wal.OperCompleted <> 'Y'
AND (wal.WorkCentre = 'PRES'
OR wal.WorkCentre = 'VPRS')
)A
INNER JOIN
(
SELECT LEFT(JobRel,6)Job,SUBSTRING(JobRel,8,2)Rel
,LocnRecordsAggregatedSummed LocnSummed
FROM
(
SELECT JobRel,SUM(LocnRecordsAggregated)LocnRecordsAggregatedSummed
FROM
(
SELECT LEFT(matl_nbr,9)JobRel,LocnRecordsAggregated
FROM
(
SELECT DISTINCT matl_nbr,LocnRecordsAggregated = 1
FROM mtrk_CompanyE.dbo.trxn_hstd
WHERE locn_to = @Locn
)A
)B
GROUP BY B.JobRel
)C
)B
ON
(LEFT(A.Job,6) = B.Job COLLATE SQL_Latin1_General_CP1_CI_AS
AND SUBSTRING(A.Job,7,1) = RIGHT(B.Rel COLLATE SQL_Latin1_General_CP1_CI_AS,1))
WHERE
LocnSummed <> QtyToMake
--AND LEFT(MReference,3) <= @FromDate - 10;
END
随着视图的更改,第 18 行和第 23 行不再出错,A 的 where 子句中的 38 "AND LEFT(wp.MReference,3) <= @FromDate" 也不再出错。但是当第 68 行取消注释时 SQL 错误:
Msg 245, Level 16, State 1, Procedure usp12DAYRESINREPORT, Line 11 [Batch Start Line 2]
Conversion failed when converting the varchar value 'P ' to data type int.
它在遇到第一个“P”时失败 MReference
,这是视图中没有最后一个 AND 子句的数据,因此我可以提取有问题的数据:
usp12DAYRESINREPORT 中的最后一个 AND 子句似乎能够看穿视图并返回原始数据和记录并在那里进行比较!?
我试图在 Access 报告前端进行最后一次比较,但得到的是#Type!使用 VAL() 将 MReference 字段转换为字符串时出现问题。
为什么最后是“AND LEFT(MReference,3) <= @FromDate - 10;”似乎一路回到 WipJobPost?或者这就是它在做什么?
这是一些有好有坏记录的数据
2000410P 2 5/8" TBI-HEAVY F DUMMIES 2020-03-11 00:00:00.000
2033480P 2 5/8" TBI-LIGHT F DUMMIES 2020-07-09 00:00:00.000
1939490P 2 5/8" TBI-LIGHT F DUMIES 2020-05-19 00:00:00.000
1932751P 2 5/8" TBI-LIGHT F DUM 2019-07-24 00:00:00.000
C139930P 2 5/8" TBI-HEAVY W/RESIN F C139930P 2020-06-10 00:00:00.000
C139930P 3 7/8" TBI-HEAVY W/RESIN F C139930P 2020-06-10 00:00:00.000
1901300P 1 3/4" TBI LAP-LIGHT W/RESIN F 94-L 2020-04-07 00:00:00.000
2070150P 2 5/8" TBI-HEAVY W/RESIN F 51-P 2020-02-21 00:00:00.000
RD83870P 2 5/8" ALI-LGT W/RESIN F 365-P 2020-01-02 00:00:00.000
1935170P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
1970701P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
1970701P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
任何帮助或解释将不胜感激。
编辑应用了修复,感谢 David Browne。
记录集显然比视图更抽象。在临时 table 中选择只有 acceptable 记录的记录集可以完成此操作。这是抽象代码:
IF OBJECT_ID('tempdb..#vwRPWIP_POST') IS NOT NULL
DROP TABLE #vwRPWIP_POST
SELECT * INTO #vwRPWIP_POST
FROM
(
SELECT TOP (100) PERCENT Job, MDescription, MWarehouse, MReference, TrnDate
FROM CompanyE.dbo.WipJobPost AS wjp
WHERE (Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (MReference LIKE '[0-9][0-9][0-9]%')
)A
更新了 FROM 子句:
FROM
#vwRPWIP_POST wp
包括临时 table 在 sp 结束时再次删除检查。
您只确保第一个字母是数字,而不是前 3 个 - 这可能是问题所在吗?
而不是
AND (LEFT(MReference, 1) LIKE '[0-9]')
我会使用类似
的东西
AND MReference LIKE '[0-9][0-9][0-9]%'
或者如果它们可能是 1 到 3 位数字,那么我会写一个小的标量值函数来 return '-' 之前的前 1 到 3 位数字的 INT
值和在视图中使用那个。
考虑
with q as
(
SELECT Job, MDescription, MWarehouse, MReference, TrnDate
FROM
CompanyE.dbo.WipJobPost AS wjp
WHERE
(Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (LEFT(MReference, 1) LIKE '[0-9]')
)
select *
from q
where LEFT(MReference,3) <= 100
这与视图类似。 SQL 服务器可以按任何顺序自由处理 WHERE 子句条件,包括在 CTE/Subquery/view.
之前应用来自外部查询的条件
您需要在临时 table 或 table 变量中实现子查询,或者编写可以重新排序的条件,例如:
case when LEFT(MReference, 1) LIKE '[0-9]' then cast(LEFT(MReference,3) as int) else 0 end <= 100
您可以尝试先评估可能的标准,例如
AND MReference between '000-' and '999-Z'
但这并不能保证。
我会尝试使用这样的函数提取 Julian 日期:
CREATE FUNCTION ExtractJulianDate(@Reference NVARCHAR(MAX))
RETURNS INT
AS
BEGIN
IF (@Reference IS NULL) RETURN NULL;
DECLARE @Token AS NVARCHAR(4);
DECLARE @DelimiterIndex AS INT;
SET @Token = LEFT(@Reference, 4);
SET @DelimiterIndex = CHARINDEX('-', @Token, 2)
-- Hardcoded to remain fast
IF (@DelimiterIndex < 2) RETURN NULL;
IF (@DelimiterIndex = 4) BEGIN
IF (@Token LIKE '[0-9][0-9][0-9]%') RETURN CAST(LEFT(@Token, 3) AS INT);
RETURN NULL;
END
IF (@DelimiterIndex = 3) BEGIN
IF (@Token LIKE '[0-9][0-9]%') RETURN CAST(LEFT(@Token, 2) AS INT);
RETURN NULL;
END
IF (@DelimiterIndex = 2) BEGIN
IF (@Token LIKE '[0-9]%') RETURN CAST(LEFT(@Token, 1) AS INT);
END
RETURN NULL;
END
用法:
SELECT Job, MDescription, MWarehouse, MReference, dbo.ExtractJulianDate(MReference) AS JulianDate, TrnDate
FROM dbo.WipJobPost AS wjp
测试:
SELECT 123 AS expected, dbo.ExtractJulianDate('123-Foo') AS actual
UNION ALL
SELECT 12 AS expected, dbo.ExtractJulianDate('12-Foo') AS actual
UNION ALL
SELECT 1 AS expected, dbo.ExtractJulianDate('1-Foo') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate(NULL) AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('-5-Foo') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('123') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('Foo') AS actual
创建了一个视图来提取数据并在多个报告中使用,vwRPWIP_POST。
SELECT TOP (100) PERCENT
Job, MDescription, MWarehouse, MReference, TrnDate
FROM
CompanyE.dbo.WipJobPost AS wjp
WHERE
(Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (LEFT(MReference, 1) LIKE '[0-9]')
它returns预期数据:
问题是当人们在 MReference
列中输入字母字符时,特别是前三个字符,SQL returns 将这些字母字符与 Julian 日期进行比较时出现错误(例如 203)。
MReference
通常包含 Julian 日期和另一个字母指示符,通常看起来像 203-MU,但将继续被允许保留违规数据。当存在违规数据时,这些记录将直接从报告中删除。
使用 T-SQL LEFT
,我将 203 与今天的 Julian 日期进行比较,进行计算并根据该结果构建适当记录的报告。有时 MReference
包含“DUMMY”、“SPACERS”、“RED-MU”等字符串。
因此,我使用 WHERE
子句中的最后一个 AND 子句对 vwRPWIP_POST
进行了修改,仅包含 MReference
中第一列为数字的记录。这行得通,当我编辑 SSMS 中的所有行时,只能看到至少第一个字符是数字的记录。
存储过程在这里:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp12DAYRESINREPORT]
@parFromDate NVARCHAR(5)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @FromDate NVARCHAR(5),@Locn NVARCHAR(10) = 'PRES';
SET @FromDate = @parFromDate;
SELECT DISTINCT
A.Job, A.JobDescription, A.QtyToMake, A.JobDeliveryDate,
A.MDescription, A.MWarehouse, A.MReference, A.HierHead1,
A.OperCompleted, A.WorkCentre,
B.LocnSummed QtyCompleted
FROM
(SELECT
wm.Job, wm.JobDescription, wm.JobDeliveryDate, wm.QtyToMake,
wp.MDescription, wp.MWarehouse, wp.MReference, wal.HierHead1
, wal.OperCompleted, wal.QtyCompleted, wal.WorkCentre
, LEFT(wm.JOB,7) AS MID7
FROM
TablesCoE.dbo.vwRPWIP_POST wp
INNER JOIN
(TablesCoE.dbo.vwRPWIP_ALLLAB wal
INNER JOIN
TablesCoE.dbo.vwRPWIP_Master wm
ON
wal.Job = wm.Job)
ON
wp.Job = wm.Job
WHERE wp.MWarehouse = 'F'
AND LEFT(wp.MReference,3) <= @FromDate
AND wal.OperCompleted <> 'Y'
AND (wal.WorkCentre = 'PRES'
OR wal.WorkCentre = 'VPRS')
)A
INNER JOIN
(
SELECT LEFT(JobRel,6)Job,SUBSTRING(JobRel,8,2)Rel
,LocnRecordsAggregatedSummed LocnSummed
FROM
(
SELECT JobRel,SUM(LocnRecordsAggregated)LocnRecordsAggregatedSummed
FROM
(
SELECT LEFT(matl_nbr,9)JobRel,LocnRecordsAggregated
FROM
(
SELECT DISTINCT matl_nbr,LocnRecordsAggregated = 1
FROM mtrk_CompanyE.dbo.trxn_hstd
WHERE locn_to = @Locn
)A
)B
GROUP BY B.JobRel
)C
)B
ON
(LEFT(A.Job,6) = B.Job COLLATE SQL_Latin1_General_CP1_CI_AS
AND SUBSTRING(A.Job,7,1) = RIGHT(B.Rel COLLATE SQL_Latin1_General_CP1_CI_AS,1))
WHERE
LocnSummed <> QtyToMake
--AND LEFT(MReference,3) <= @FromDate - 10;
END
随着视图的更改,第 18 行和第 23 行不再出错,A 的 where 子句中的 38 "AND LEFT(wp.MReference,3) <= @FromDate" 也不再出错。但是当第 68 行取消注释时 SQL 错误:
Msg 245, Level 16, State 1, Procedure usp12DAYRESINREPORT, Line 11 [Batch Start Line 2]
Conversion failed when converting the varchar value 'P ' to data type int.
它在遇到第一个“P”时失败 MReference
,这是视图中没有最后一个 AND 子句的数据,因此我可以提取有问题的数据:
usp12DAYRESINREPORT 中的最后一个 AND 子句似乎能够看穿视图并返回原始数据和记录并在那里进行比较!?
我试图在 Access 报告前端进行最后一次比较,但得到的是#Type!使用 VAL() 将 MReference 字段转换为字符串时出现问题。
为什么最后是“AND LEFT(MReference,3) <= @FromDate - 10;”似乎一路回到 WipJobPost?或者这就是它在做什么?
这是一些有好有坏记录的数据
2000410P 2 5/8" TBI-HEAVY F DUMMIES 2020-03-11 00:00:00.000
2033480P 2 5/8" TBI-LIGHT F DUMMIES 2020-07-09 00:00:00.000
1939490P 2 5/8" TBI-LIGHT F DUMIES 2020-05-19 00:00:00.000
1932751P 2 5/8" TBI-LIGHT F DUM 2019-07-24 00:00:00.000
C139930P 2 5/8" TBI-HEAVY W/RESIN F C139930P 2020-06-10 00:00:00.000
C139930P 3 7/8" TBI-HEAVY W/RESIN F C139930P 2020-06-10 00:00:00.000
1901300P 1 3/4" TBI LAP-LIGHT W/RESIN F 94-L 2020-04-07 00:00:00.000
2070150P 2 5/8" TBI-HEAVY W/RESIN F 51-P 2020-02-21 00:00:00.000
RD83870P 2 5/8" ALI-LGT W/RESIN F 365-P 2020-01-02 00:00:00.000
1935170P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
1970701P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
1970701P 2 5/8" TBI-HEAVY W/RESIN F 365-P 2020-01-02 00:00:00.000
任何帮助或解释将不胜感激。
编辑应用了修复,感谢 David Browne。
记录集显然比视图更抽象。在临时 table 中选择只有 acceptable 记录的记录集可以完成此操作。这是抽象代码:
IF OBJECT_ID('tempdb..#vwRPWIP_POST') IS NOT NULL
DROP TABLE #vwRPWIP_POST
SELECT * INTO #vwRPWIP_POST
FROM
(
SELECT TOP (100) PERCENT Job, MDescription, MWarehouse, MReference, TrnDate
FROM CompanyE.dbo.WipJobPost AS wjp
WHERE (Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (MReference LIKE '[0-9][0-9][0-9]%')
)A
更新了 FROM 子句:
FROM
#vwRPWIP_POST wp
包括临时 table 在 sp 结束时再次删除检查。
您只确保第一个字母是数字,而不是前 3 个 - 这可能是问题所在吗?
而不是
AND (LEFT(MReference, 1) LIKE '[0-9]')
我会使用类似
的东西AND MReference LIKE '[0-9][0-9][0-9]%'
或者如果它们可能是 1 到 3 位数字,那么我会写一个小的标量值函数来 return '-' 之前的前 1 到 3 位数字的 INT
值和在视图中使用那个。
考虑
with q as
(
SELECT Job, MDescription, MWarehouse, MReference, TrnDate
FROM
CompanyE.dbo.WipJobPost AS wjp
WHERE
(Job LIKE '%P')
AND (MWarehouse = 'F')
AND (TrnDate > DATEADD(DAY, - 395, GETDATE()))
AND (LEFT(MReference, 1) LIKE '[0-9]')
)
select *
from q
where LEFT(MReference,3) <= 100
这与视图类似。 SQL 服务器可以按任何顺序自由处理 WHERE 子句条件,包括在 CTE/Subquery/view.
之前应用来自外部查询的条件您需要在临时 table 或 table 变量中实现子查询,或者编写可以重新排序的条件,例如:
case when LEFT(MReference, 1) LIKE '[0-9]' then cast(LEFT(MReference,3) as int) else 0 end <= 100
您可以尝试先评估可能的标准,例如
AND MReference between '000-' and '999-Z'
但这并不能保证。
我会尝试使用这样的函数提取 Julian 日期:
CREATE FUNCTION ExtractJulianDate(@Reference NVARCHAR(MAX))
RETURNS INT
AS
BEGIN
IF (@Reference IS NULL) RETURN NULL;
DECLARE @Token AS NVARCHAR(4);
DECLARE @DelimiterIndex AS INT;
SET @Token = LEFT(@Reference, 4);
SET @DelimiterIndex = CHARINDEX('-', @Token, 2)
-- Hardcoded to remain fast
IF (@DelimiterIndex < 2) RETURN NULL;
IF (@DelimiterIndex = 4) BEGIN
IF (@Token LIKE '[0-9][0-9][0-9]%') RETURN CAST(LEFT(@Token, 3) AS INT);
RETURN NULL;
END
IF (@DelimiterIndex = 3) BEGIN
IF (@Token LIKE '[0-9][0-9]%') RETURN CAST(LEFT(@Token, 2) AS INT);
RETURN NULL;
END
IF (@DelimiterIndex = 2) BEGIN
IF (@Token LIKE '[0-9]%') RETURN CAST(LEFT(@Token, 1) AS INT);
END
RETURN NULL;
END
用法:
SELECT Job, MDescription, MWarehouse, MReference, dbo.ExtractJulianDate(MReference) AS JulianDate, TrnDate
FROM dbo.WipJobPost AS wjp
测试:
SELECT 123 AS expected, dbo.ExtractJulianDate('123-Foo') AS actual
UNION ALL
SELECT 12 AS expected, dbo.ExtractJulianDate('12-Foo') AS actual
UNION ALL
SELECT 1 AS expected, dbo.ExtractJulianDate('1-Foo') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate(NULL) AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('-5-Foo') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('123') AS actual
UNION ALL
SELECT NULL AS expected, dbo.ExtractJulianDate('Foo') AS actual