I need this working query as a stored procedure where I can give two databases (one masterdb one testdb), start chainage, end chainage and UniqueRun as input
DECLARE @StartChainage FLOAT = 17.00;
DECLARE @EndChainage FLOAT = 20.00;
WITH MasterDistress AS
(
SELECT
DR.IDDistressRecord,
DS.IDSession,
DT.DistressTypeName,
S.SeverityName,
geometry::STPolyFromText(
'POLYGON((' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) + ', ' +
CAST(DR.EndX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) + ', ' +
CAST(DR.EndX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + ', ' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + ', ' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) +
'))', 0
).MakeValid() AS geom,
LA25_00230133_Dis_Batch002A.dbo.fn_GetCollectedChainage(DS.IDSession, DR.DistanceStamp) AS Chainage
FROM LA25_00230133_Dis_Batch002A.distress.DistressRecords DR
JOIN LA25_00230133_Dis_Batch002A.dbo.DCSessions DS ON DR.IDSession = DS.IDSession
JOIN LA25_00230133_Dis_Batch002A.distress.DistressTypes DT ON DR.IDDistressType = DT.IDDistressType
JOIN LA25_00230133_Dis_Batch002A.distress.Severities S ON DR.IDSeverity = S.IDSeverity
WHERE DS.UniqueRun = '51H0VRTI'
),
TestDistress AS
(
SELECT
DR.IDDistressRecord,
DT.DistressTypeName,
S.SeverityName,
geometry::STPolyFromText(
'POLYGON((' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) + ', ' +
CAST(DR.EndX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) + ', ' +
CAST(DR.EndX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + ', ' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + ', ' +
CAST(DR.StartX AS VARCHAR(20)) + ' ' + CAST(DR.DistanceStamp AS VARCHAR(20)) +
'))', 0
).MakeValid() AS geom,
LA25_00230133_Dis_Batch002A_TRN.dbo.fn_GetCollectedChainage(DS.IDSession, DR.DistanceStamp) AS Chainage
FROM LA25_00230133_Dis_Batch002A_TRN.distress.DistressRecords DR
JOIN LA25_00230133_Dis_Batch002A_TRN.dbo.DCSessions DS ON DR.IDSession = DS.IDSession
JOIN LA25_00230133_Dis_Batch002A_TRN.distress.DistressTypes DT ON DR.IDDistressType = DT.IDDistressType
JOIN LA25_00230133_Dis_Batch002A_TRN.distress.Severities S ON DR.IDSeverity = S.IDSeverity
WHERE DS.UniqueRun = '51H0VRTI'
)
SELECT
M.Chainage,
ROUND(
ISNULL(
M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0),
0
) * 100, 1
) AS AccuracyPercent,
CASE
WHEN T.geom IS NULL THEN 'Distress Missing'
WHEN T.geom.STArea() > M.geom.STArea() * 1.5 THEN 'Area Mismatch'
WHEN M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) * 100 < 50 THEN 'Area Mismatch'
WHEN M.DistressTypeName <> T.DistressTypeName THEN 'Distress Type Mismatch'
WHEN M.SeverityName <> T.SeverityName THEN 'Distress Severity Mismatch'
ELSE '-'
END AS DefectType,
CASE
WHEN T.geom IS NULL THEN 'Distress Not Marked'
WHEN T.geom.STArea() > M.geom.STArea() * 1.5 THEN 'Distress area larger than expected'
WHEN M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) * 100 < 50 THEN 'Distress area too small or off-target'
WHEN M.DistressTypeName <> T.DistressTypeName THEN 'Distress Type ' + M.DistressTypeName + ' expected'
WHEN M.SeverityName <> T.SeverityName THEN 'Distress Severity ' + M.SeverityName + ' expected'
ELSE '--No issues--'
END AS Issue
FROM
MasterDistress M
OUTER APPLY
(SELECT TOP 1 T.*
FROM TestDistress T
WHERE T.geom.STIsValid() = 1
AND M.geom.STIntersects(T.geom) = 1
ORDER BY M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) DESC) T
WHERE
M.Chainage BETWEEN @StartChainage AND @EndChainage
UNION ALL
-- Extra Distress
SELECT
T.Chainage,
0 AS AccuracyPercent,
'Extra Distress' AS DefectType,
'Distress marked unnecessarily' AS Issue
FROM
TestDistress T
WHERE
NOT EXISTS (SELECT 1
FROM MasterDistress M
WHERE M.geom.STIntersects(T.geom) = 1)
AND T.Chainage BETWEEN @StartChainage AND @EndChainage
ORDER BY
Chainage;
But when I try to make it dynamic, I get tons of repetitive syntax errors which don't seem to resolve
CREATE PROCEDURE sp_CompareDistressMasterVsTest
@MasterDB NVARCHAR(100),
@TestDB NVARCHAR(100),
@UniqueRun NVARCHAR(50),
@StartChainage FLOAT,
@EndChainage FLOAT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = '
WITH MasterDistress AS (
SELECT
DR.IDDistressRecord,
DS.IDSession,
DT.DistressTypeName,
S.SeverityName,
geometry::STPolyFromText(
''POLYGON(('' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) + '', '' +
CAST(DR.EndX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) + '', '' +
CAST(DR.EndX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + '', '' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + '', '' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) +
''))'', 0
).MakeValid() AS geom,
' + QUOTENAME(@MasterDB) + '.dbo.fn_GetCollectedChainage(DS.IDSession, DR.DistanceStamp) AS Chainage
FROM ' + QUOTENAME(@MasterDB) + '.distress.DistressRecords DR
JOIN ' + QUOTENAME(@MasterDB) + '.dbo.DCSessions DS ON DR.IDSession = DS.IDSession
JOIN ' + QUOTENAME(@MasterDB) + '.distress.DistressTypes DT ON DR.IDDistressType = DT.IDDistressType
JOIN ' + QUOTENAME(@MasterDB) + '.distress.Severities S ON DR.IDSeverity = S.IDSeverity
WHERE DS.UniqueRun = @UniqueRun
),
TestDistress AS (
SELECT
DR.IDDistressRecord,
DT.DistressTypeName,
S.SeverityName,
geometry::STPolyFromText(
''POLYGON(('' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) + '', '' +
CAST(DR.EndX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) + '', '' +
CAST(DR.EndX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + '', '' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp + DR.Length AS VARCHAR(20)) + '', '' +
CAST(DR.StartX AS VARCHAR(20)) + '' '' + CAST(DR.DistanceStamp AS VARCHAR(20)) +
''))'', 0
).MakeValid() AS geom,
' + QUOTENAME(@TestDB) + '.dbo.fn_GetCollectedChainage(DS.IDSession, DR.DistanceStamp) AS Chainage
FROM ' + QUOTENAME(@TestDB) + '.distress.DistressRecords DR
JOIN ' + QUOTENAME(@TestDB) + '.dbo.DCSessions DS ON DR.IDSession = DS.IDSession
JOIN ' + QUOTENAME(@TestDB) + '.distress.DistressTypes DT ON DR.IDDistressType = DT.IDDistressType
JOIN ' + QUOTENAME(@TestDB) + '.distress.Severities S ON DR.IDSeverity = S.IDSeverity
WHERE DS.UniqueRun = @UniqueRun
)
SELECT
M.Chainage,
ROUND(
ISNULL(
M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0),
0
) * 100, 1
) AS AccuracyPercent,
CASE
WHEN T.geom IS NULL THEN ''Distress Missing''
WHEN T.geom.STArea() > M.geom.STArea() * 1.5 THEN ''Area Mismatch''
WHEN M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) * 100 < 50 THEN ''Area Mismatch''
WHEN M.DistressTypeName <> T.DistressTypeName THEN ''Distress Type Mismatch''
WHEN M.SeverityName <> T.SeverityName THEN ''Distress Severity Mismatch''
ELSE ''-''
END AS DefectType,
CASE
WHEN T.geom IS NULL THEN ''Distress Not Marked''
WHEN T.geom.STArea() > M.geom.STArea() * 1.5 THEN ''Distress area larger than expected''
WHEN M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) * 100 < 50 THEN ''Distress area too small or off-target''
WHEN M.DistressTypeName <> T.DistressTypeName THEN ''Distress Type '' + M.DistressTypeName + '' expected''
WHEN M.SeverityName <> T.SeverityName THEN ''Distress Severity '' + M.SeverityName + '' expected''
ELSE ''--No issues--''
END AS Issue
FROM MasterDistress M
OUTER APPLY (
SELECT TOP 1 T.*
FROM TestDistress T
WHERE T.geom.STIsValid() = 1
AND M.geom.STIntersects(T.geom) = 1
ORDER BY M.geom.STIntersection(T.geom).STArea() / NULLIF(M.geom.STUnion(T.geom).STArea(), 0) DESC
) T
WHERE M.Chainage BETWEEN @StartChainage AND @EndChainage
UNION ALL
SELECT
T.Chainage,
0 AS AccuracyPercent,
''Extra Distress'' AS DefectType,
''Distress marked unnecessarily'' AS Issue
FROM TestDistress T
WHERE NOT EXISTS (
SELECT 1
FROM MasterDistress M
WHERE M.geom.STIntersects(T.geom) = 1
)
AND T.Chainage BETWEEN @StartChainage AND @EndChainage
ORDER BY Chainage;
'
EXEC sp_executesql
@SQL,
N'@UniqueRun NVARCHAR(50), @StartChainage FLOAT, @EndChainage FLOAT',
@UniqueRun = @UniqueRun,
@StartChainage = @StartChainage,
@EndChainage = @EndChainage;
END;
EXEC sp_CompareDistressMasterVsTest
@MasterDB = 'LA25_00230133_Dis_Batch002A',
@TestDB = 'LA25_00230133_Dis_Batch002A_TRN',
@UniqueRun = '51H0VRTI',
@StartChainage = 17.0,
@EndChainage = 20.0;
Msg 105, Level 15, State 1, Line 183
Unclosed quotation mark after the character string 'Dis'.Msg 102, Level 15, State 1, Line 183
Incorrect syntax near 'Dis'.
How do I make this query as a working stored procedure?
PRINT/SELECTthe statement first. Then you can debug that SQL and solve the problem before propagating the solution to your SQL that generates the dynamic statement. Often you'll find that the problems are quite simple, such as a typographical error that is difficult to spot in the literal strings; for example a missing whitespace/linebreak, or leading/trailing delimiters. Taking the time to get the non-dynamic statement working first is really important, as if that doesn't work the dynamic one will have no chance of working correctly.QUOTENAMEto quote the database names, and their variables are parameterised. I don't actually see any injection issues. The only additional thing they could do would be the validate the names of the databases againstsys.databases, and in the event it's not a real database, not run anything, but they would still need useQUOTENAMEagainst thenamecolumn instead.sp_prefix for your stored procedures. Microsoft has reserved that prefix for its own use (see Naming Stored Procedures), and you do run the risk of a name clash sometime in the future. It's also bad for your stored procedure performance. It's best to just simply avoidsp_and use something else as a prefix - or no prefix at all!