Pivot query in SQL Server - sql-server

I want to build dynamic sql query based on following
SELECT P.Trackingno,PA.SKUId,SC.SKUName,count(PA.SKUId) as TotalSKUId
,sum(case when PA.IsAvailable = 1 then 1 else 0 end) AS IsAvailable
FROM ADMIN.posavailability PA
LEFT OUTER JOIN Admin.SKUCreation SC ON SC.TCID=PA.Skuid
LEFT OUTER JOIN Admin.POSVisitDetails PD on PD.VisitId=PA.VisitID
LEFT OUTER JOIN Admin.POS P ON P.TrackingNo=PD.TrackingNo
WHERE PA.VisitId in
(SELECT visitid FROM Admin.POSVisitDetails PD WHERE PD.month=2 and PD.year=2017)
and PA.IsActive=1
GROUP BY P.Trackingno,PA.SKUId,SC.SKUName,PD.Month,PD.year
ORDER BY P.Trackingno
I got out as follows:
My desired output is:
Can any one help for dynamic pivot SQL query.

Here maybe help you.
Dynamic sql query
CREATE TABLE TrackingTbl
(
TrackingNo int,
SKUID int,
SKUName varchar(50),
TotalSKUID int,
IsAvaiable int
)
INSERT INTO TrackingTbl VALUES (1234,1,'Red',2,2)
INSERT INTO TrackingTbl VALUES (1234,2,'White',2,1)
INSERT INTO TrackingTbl VALUES (1234,3,'Blue',2,0)
INSERT INTO TrackingTbl VALUES (1234,4,'Yellow',2,2)
INSERT INTO TrackingTbl VALUES (3456,1,'Red',3,3)
INSERT INTO TrackingTbl VALUES (3456,2,'White',3,2)
INSERT INTO TrackingTbl VALUES (3456,3,'Blue',3,1)
INSERT INTO TrackingTbl VALUES (3456,4,'Yellow',3,0)
DECLARE #Columns varchar(200)
SET #Columns = Stuff((SELECT concat(', [',td.SKUName,']') FROM (select DISTINCT tt.SKUName FROM TrackingTbl tt ) td FOR XML PATH (''))
,1,1,'')
DECLARE #Query nvarchar(max) = CONCAT(
'SELECT TrackingNo,TotalSKUID,',#Columns,
' FROM
(
SELECT tt.IsAvaiable, tt.SKUName ,tt.TrackingNo, tt.TotalSKUID
FROM TrackingTbl tt
) sc
PIVOT
(
sum(IsAvaiable) FOR SKuName IN (',#Columns,' )
) pvt')
exec sp_executesql #Query
DROP TABLE dbo.TrackingTbl

Try this using MAX ()
;with cte as (
SELECT P.Trackingno,PA.SKUId,SC.SKUName,count(PA.SKUId) as TotalSKUId
,sum(case when PA.IsAvailable = 1 then 1 else 0 end) AS IsAvailable
FROM ADMIN.posavailability PA
LEFT OUTER JOIN Admin.SKUCreation SC ON SC.TCID=PA.Skuid
LEFT OUTER JOIN Admin.POSVisitDetails PD on PD.VisitId=PA.VisitID
LEFT OUTER JOIN Admin.POS P ON P.TrackingNo=PD.TrackingNo
WHERE PA.VisitId in
(SELECT visitid FROM Admin.POSVisitDetails PD WHERE PD.month=2 and PD.year=2017)
and PA.IsActive=1
GROUP BY P.Trackingno,PA.SKUId,SC.SKUName,PD.Month,PD.year
ORDER BY P.Trackingno
)
select trackingno,totalskuid,max(case when skuname='Red' then isavailable else '' end) Red,
max(case when skuname='White' then isavailable else '' end) White,
max(case when skuname='Blue' then isavailable else '' end) Blue,
max(case when skuname='Yellow' then isavailable else '' end) Yellow
from cte
group by trackingno,totalskuid

Related

Using DECLARE while creating a VIEW?

The literature says that the declare statement is not compatible with creating a View. How do I get around it?
My declare statement looks like:
DECLARE #risk_5 TABLE (Code VARCHAR(100));
INSERT INTO #risk_5 (Code) VALUES ('AA'),('BB'),('CC');
and is then used within a select statement:
SELECT
id,
CASE
WHEN a.[10_2_1_Country] IN (SELECT Code from #risk_5)
THEN '3'
END AS Risk_Country5
FROM x
The recommendation is to pack the declare into a CTE or a stored procedure.
With both these recommendations though, I do not understand how to connect the two? What am I missing?
If you need to use variable try to use stored procedures, if you write a select query in the stored procedure you can get the data too. And you can use declare inside.
I use this way in my solution e.g.
CREATE PROCEDURE [dbo].[GetLoadSiteMass]( #month INT,
#year int,
#storageId int,
#parent nvarchar(50),
#materialSourceId nvarchar(100),
#complexIds nvarchar(50))
AS
BEGIN
DECLARE #MonthPrev int
DECLARE #YearPrev int
SET #MonthPrev = CASE WHEN #Month = 1 THEN 12 ELSE #Month - 1 END
SET #YearPrev = CASE WHEN #Month = 1 THEN #Year - 1 ELSE #Year END
declare #WagonLoadSiteId int
set #WagonLoadSiteId = (select top 1 CarriageLoadSiteId from CarriageLoadSite where LoadSiteType = 2);
DECLARE #loadSide nvarchar(10), #result decimal(18,3)
SET #loadSide=cast( #storageId as nvarchar(50));
WITH CarriageLoadSiteTreeView (
[CarriageLoadSiteId],RootId,RootName,[Code], Name, ParentID, [LoadSiteType],IsDelete,
CodeSAP,DepartmentId, Capacity, MinLimit, MaxLimit, LoadSitePlaceTypeId) AS
(
SELECT [CarriageLoadSiteId],
[CarriageLoadSiteId] RootId,
Name RootName,
[Code],
Name,
ParentID,
[LoadSiteType],
[IsDelete],
CodeSAP,
DepartmentId,
Capacity,
MinLimit,
MaxLimit,
LoadSitePlaceTypeId
FROM CarriageLoadSite WITH(NOLOCK)
WHERE ISNULL(ParentID,0) =isnull(#storageId,0) AND Isdelete!=1
UNION ALL
SELECT d.[CarriageLoadSiteId],
q.RootId RootId,
RootName RootName,
d.[Code],
d.Name,
d.ParentID,
d.[LoadSiteType],
d.[IsDelete],
d.CodeSAP,
d.DepartmentId,
d.Capacity,
d.MinLimit,
d.MaxLimit,
d.LoadSitePlaceTypeId
FROM CarriageLoadSite AS d WITH(NOLOCK)
INNER JOIN CarriageLoadSiteTreeView AS q ON d.ParentID = q.[CarriageLoadSiteId] WHERE d.IsDelete!=1
)
SELECT
ComplexId,
RootId Id,
cast(RootId as nvarchar(8))+'|Sclad'+IIF(RootId=max(R.CarriageLoadSiteId),'|finish','') [Uid],
RootName CarriageLoadSiteName,
ROUND(SUM(AMOUNT-movement-consumption)/1000,3) Amount,
cast(1 as bit) hasChildren,
T.FullPathId Path,
UparentId=IIF(#parent is null,'',#parent),
[Type]=0,
Petal = IIF(RootId=max(R.CarriageLoadSiteId),'|Sclad|finish','')
FROM (
SELECT
RootId
,RootName
,t.CarriageLoadSiteId
,t.MaterialId
,YEAR(t.Period) [Year]
,MONTH(t.Period) [Month]
,round( case when (t.Amount=0 or t.Amount is null) and (tt.Type=0 or [TypeAmountCarriage]=1 )then carr.[CertifNetto]else t.Amount end ,0 )[Amount]
,t.UnitId
, CarriageId
, tt.TurnoverTypeId
,round(dbo.GetMovementByTurnOverWithTempValue(t.turnoverid),5) movement
,dbo.GetConsumptionByTurnOver(t.turnoverid) consumption
,0 stockBegin
,round(t.Amount,0 ) CommingAmount
,case when (t.Amount=0 or t.Amount is null) and tt.Type=0 then 1 else 0 end [IsNotConfirmed]
,[TypeAmountCarriage]
,M.ComplexId
FROM Turnover t WITH(NOLOCK)
INNER JOIN TurnoverType tt ON tt.TurnoverTypeId = t.TurnoverTypeId
INNER JOIN CarriageLoadSiteTreeView l ON l.CarriageLoadSiteId = t.CarriageLoadSiteId
INNER JOIN [Carriages] carr on carr.[CarriagesID]=t.[CarriageId]
INNER JOIN Material M on M.MaterialID=t.MaterialId
WHERE YEAR(t.Period) = #Year AND
MONTH(t.Period) = #Month AND
l.LoadSiteType = 0 AND
tt.type in (0,5,4) AND
isclear=0 AND
M.MaterialSourceID in (select value from string_split(#materialSourceId, ','))
UNION ALL
SELECT RootId
,RootName
,s.CarriageLoadSiteId
,s.MaterialId
,#Year [Year]
,#Month [Month]
,round(s.Amount,0)
,s.UnitId
,CarriageId
,[TurnoverTypeId]
,round(dbo.GetMovementByStock(s.StockId),5) movement
,dbo.GetConsumptionByStock(s.StockId) consumption
,round(s.Amount,0)-s.spendStock
,0
,0 [IsNotConfirmed]
,[TypeAmountCarriage]
,M.ComplexId
FROM Stock s
INNER JOIN CarriageLoadSiteTreeView l ON l.CarriageLoadSiteId = s.CarriageLoadSiteId
INNER JOIN Material M on M.MaterialID=s.MaterialId
WHERE s.[Year] = #YearPrev AND
s.[Month] = #MonthPrev AND
s.[Type] = 0 AND
l.LoadSiteType = 0 AND
amount >0 AND
isclear=0 AND
M.MaterialSourceID in (select value from string_split(#materialSourceId, ','))
) as R
INNER JOIN CariageLoadSiteTree T on T.CarriageLoadSiteId=RootId
INNER JOIN string_split(#complexIds, ',') MM ON CAST(MM.value AS int) = R.ComplexId
WHERE AMOUNT-movement-consumption>10
GROUP BY RootName,RootId,ComplexId, T.FullPathId
ORDER BY RootName

How to get cumulative record In SQL Server?

I have some data in SQL Server in below format.
declare #statement table
(
acctno int,
statDate date,
tod int,
lastDate date
)
insert into #statement select 123,'2018-02-12',567,'2018-01-12'
insert into #statement select 123,'2018-03-12',580,'2018-02-12'
insert into #statement select 123,'2018-04-12',567,'2018-03-12'
--select * from #statement
declare #txn table
(
acct int,
txndate date,
amount int
)
insert into #txn select 123,'2018-02-11',400
insert into #txn select 123,'2018-02-18',400
insert into #txn select 123,'2018-02-25',400
insert into #txn select 123,'2018-03-11',400
insert into #txn select 123,'2018-03-25',400
Result of the both tables similar like below.
]1
Now I want the result as shown here:
and I am trying to get it with this query:
;with cte as
(
select
acctno, statDate, tod, txndate, amount, lastDate
from
#statement
inner join
#txn on acctno = acct
)
select *
from #txn t
left join cte on acct = acctno
and t.txndate between statDate and lastDate
But the result is not being returned as expected - please help me get the desired result.
I think that you can do this more simply
select s.acctno,statDate,tod,txndate,amount,lastDate
from #txn t
Inner join #statement s
On s.acctno = t.acct
And t.txndate Between s.lastDate and s.startDate
You may need to change the BETWEEN to a “<“ for the startDate comparison.
This is a clasical DKNF join :
WITH TD AS(
SELECT T1.acctno, T1.statDate AS StartDate, T1.tod, COALESCE(MAX(T2.statDate), '1-01-01') AS BeforeDate
FROM #statement AS T1
LEFT OUTER JOIN #statement AS T2
ON T1.acctno = T2.acctno AND T1.statDate < T2.statDate
GROUP BY T1.acctno, T1.statDate, T1.tod
)
SELECT acctno, StartDate, tod, txndate, amount
FROM TD
JOIN #txn AS t
ON TD.acctno = t.acct AND txndate <= StartDate AND txndate > BeforeDate

Pivot with Dynamic sql as Parameter

I am not sure if this is the way to go though but I would like to know if there is someway to have a stored procedure and pass in a queries to get the values to pivot. I'm not sure if this is a good idea, so sorry if this is a stupid question, but it would be great to pass in a query instead of hard coding every single pivot you want. I have an example of the pivot Stored Procedure that I have coded. This also includes the grand totals for rows and columns.
Don't know if I should add the code as well?
Hope this makes sense.
Please see my stored procedure code below:
CREATE PROCEDURE [dbo].[PivotNoAgentPerc_SP]
AS
DECLARE #columnHeaders VARCHAR (MAX)
SELECT #columnHeaders = COALESCE(#columnHeaders + ', ','')+ QUOTENAME(granteddate)
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
) AS B
ORDER BY B.granteddate
/* GRAND TOTAL COLUMN */
DECLARE #GrandTotalCol NVARCHAR (MAX)
SELECT #GrandTotalCol =
COALESCE (#GrandTotalCol + 'ISNULL ([' + CAST (granteddate AS VARCHAR) +'],0) + ', 'ISNULL([' + CAST(granteddate AS VARCHAR)+ '],0) + ')
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
) AS B
ORDER BY B.granteddate
SET #GrandTotalCol = LEFT (#GrandTotalCol, LEN (#GrandTotalCol)-1)
/* GRAND TOTAL ROW */
DECLARE #GrandTotalRow NVARCHAR(MAX)
SELECT #GrandTotalRow =
COALESCE(#GrandTotalRow + ',ISNULL(SUM([' + CAST(granteddate AS VARCHAR)+']),0)', 'ISNULL(SUM([' + CAST(granteddate AS VARCHAR)+']),0)')
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
)AS B
ORDER BY B.granteddate
/* MAIN QUERY */
DECLARE #FinalQuery NVARCHAR (MAX)
SET #FinalQuery = 'SELECT *, ('+ #GrandTotalCol + ')
AS [Grand Total] INTO #temp_MatchesTotal
FROM
(SELECT
ISNULL(ConsultantUser.user_name,''Total'') AS [Consultant],
EOMONTH(BondSales.granteddate,0) AS [Month Granted],
COALESCE(CAST(CAST(SUM(CASE WHEN accounts_1.name = ''No Agent Channel'' THEN 1 ELSE 0 END) AS DECIMAL)/COUNT(BondSales.name) AS decimal(5,2)), 0) AS NoAgentPerc
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = ''Coastal''
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
GROUP BY EOMONTH(BondSales.granteddate, 0), ConsultantUser.user_name
) A
PIVOT
(
SUM(NoAgentPerc)
FOR [Month Granted]
IN ('+#columnHeaders +')
) B
ORDER BY [Consultant]
SELECT * FROM #temp_MatchesTotal
UNION ALL
SELECT ''Grand Total'','+ #GrandTotalRow +', ISNULL (SUM([Grand Total]),0)
FROM #temp_MatchesTotal
DROP TABLE #temp_MatchesTotal'
EXECUTE(#FinalQuery)
Sorry if the code is long but I just wanted to show you exactly what I an talking about
I have done this is in procedure and it is working fine. Compare your example to below code
You have to write Dynamic sql query
Example :
declare #s date
declare #e date
declare #dates varchar(MAX)
set #s=DATEADD(DAY,-1,#startdate)
set #e=#enddate
IF OBJECT_ID('tempdb.#DATES') IS NOT NULL
DROP TABLE #DATES
CREATE TABLE #DATES
(
ID INT PRIMARY KEY IDENTITY(1,1),
Date VARCHAR(MAX)
)
WHILE (#s<#e)
BEGIN
INSERT INTO #DATES (Date)
select CONCAT( '[',DATEADD(DAY ,1, #s),']')
set #s=DATEADD(DAY ,1, #s)
END
SELECT #dates= STUFF((
SELECT ',' + SPACE(1) + Date
FROM #DATES
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
--AS [Dates]
------------------------Getting RDW Datewise--------------------------------------
declare #queryRDW VARCHAR(MAX)
set #queryRDW='
select * from (
SELECT SUM(RecordValue)/60 RecordValue,RecordType ,Computationdate ,t2.ProjectResourceId,t1.ResourceCode,t1.ResourceName
FROM tblProjectResource t1 INNER JOIN tblDeploymentWorkbook t2 on t1.ProjectResourceId=t2.ProjectResourceId
WHERE
RecordType=''RDW'' and t2.SubCategoryId IN (1)
GROUP BY
RecordType, Computationdate,t2.ProjectResourceId,t1.resourceCode,t1.ResourceName
HAVING
Computationdate >= '''+Cast(#startdate as varchar)+''' and Computationdate <= '''+Cast(#enddate as varchar)+'''
)tab
PIVOT(
SUM(RecordValue) for ComputationDate in ( '+#dates+')
)t
Order By ResourceName
'
exec (#queryRDW)

selecting random records inside a UDF

I'm writing a function in sql server 2012.
I came to know that we can not use RAND() or NEWID() functions in the select statement of a function in sql server.
My function goes like this:
CREATE FUNCTION Keywordsuggester (#userid INT)
returns #suggestor_tab TABLE (
keywordid INT,
keywordname VARCHAR(max),
keywordcategory VARCHAR(max))
AS
BEGIN
DECLARE #category_table TABLE
(
category_name VARCHAR(max),
category_id INT,
rownum INT
)
DECLARE #ID INT = 1
DECLARE #COUNT INT = 0
DECLARE #I INT = 1
INSERT INTO #category_table
SELECT kc.NAME,
kc.id,
k.NAME,
d.NAME,
Row_number()
OVER(
ORDER BY d.id ASC) AS rownum
FROM dtypes d
JOIN keywords k
ON d.NAME LIKE '%' + k.NAME + '%'
JOIN keywordscategory kc
ON k.categoryid = kc.id
WHERE d.userid = #userid
SELECT #count = rownum
FROM #category_table
WHILE #count > #I
BEGIN
INSERT INTO #suggestor_tab
SELECT TOP 5 kc.id,
k.NAME,
kc.NAME
FROM kwords k
JOIN #category_table ct
ON k.categoryid = ct.category_id
JOIN kwcategory kc
ON kc.NAME = ct.category_name
WHERE ct.rownum = #I
--Here I'm inserting top 5 records for each category into the suggestor_tab,instead I have to insert random 5 records for each category(i.e.,#I)
SET #I=#I + 1
--return
END
INSERT INTO #suggestor_tab
SELECT kc.id,
k.NAME,
kc.NAME
FROM kwords k
JOIN #category_table ct
ON k.categoryid = ct.category_id
JOIN kwcategory kc
ON kc.NAME = category_name
RETURN
END
How can I get random records for each category(i.e., #I in the while loop).
I tried the query like:
SELECT TOP 5 k.NAME
FROM kwords k
JOIN #category_table ct
ON k.category_id = ct.id
JOIN kwcategory kc
ON kc.NAME = category_name
WHERE ct.rownum = #I
ORDER BY Newid()
which throws an error like:
Invalid use of a side-effecting operator 'newid' within a function.
Is there anyway to do this?
Thanks in advance.
You cannot use Non-deterministic Funcions inside UDF.
Create a View and use it in order by
create view Random
as
select newid() as New_id
Change you select something like this.
SELECT TOP 5 k.NAME
FROM KWords k
JOIN #category_table ct
ON k.category_id = ct.id
JOIN kwcategory kc
ON kc.NAME = category_name
WHERE ct.rownum = #I
ORDER BY (SELECT new_id
FROM random)

t-sql: dynamically filter XML on multiple conditions?

I'm trying to find a way to do a accept/reject on an XML string, by joining it to a table of conditions. I have one "filter" working now, but want to write it so that it can filter 2 or more.
Here's code that matches one of the two. If either matches, it will filter the string.
What I want to do is make it so it has to match BOTH, while still leaving the option for single-condition
CREATE TABLE #filter (exclusion_type CHAR(1), excluded_value varchar(10))
INSERT INTO #filter VALUES ('B','boy')
INSERT INTO #filter VALUES ('C','cat')
DECLARE #data XML
SELECT #data = '<A><B>boy</B><C>cat</C></A>'
SELECT * FROM (SELECT CONVERT(VARCHAR(128),node.query('fn:local-name(.)')) AS NodeName, CONVERT(VARCHAR(MAX),node.query('./text()')) AS NodeValue
FROM #data.nodes(N'//*') T(node))xml_shred
IF NOT EXISTS
(SELECT * FROM (SELECT CONVERT(VARCHAR(128),node.query('fn:local-name(.)')) AS NodeName, CONVERT(VARCHAR(MAX),node.query('./text()')) AS NodeValue
FROM #data.nodes(N'//*') T(node)) xml_shred
INNER JOIN #filter
ON (nodename = exclusion_type AND nodevalue LIKE excluded_value)
)
select 'record would be inserted '
ELSE select 'record was filtered'
Here's how I currently have it to filter both. Ugly and non-expandable.
IF NOT EXISTS
(SELECT * FROM (SELECT CONVERT(VARCHAR(128),node.query('fn:local-name(.)')) AS NodeName, CONVERT(VARCHAR(MAX),node.query('./text()')) AS NodeValue
FROM #data.nodes(N'//*') T(node)) xml_shred
INNER JOIN #filter
ON (nodename = exclusion_type AND nodevalue LIKE excluded_value)
)
--combination filters don't easily work within that xml_shred
and not(
#data.value('(/A/B)[1]', 'varchar(128)') = 'boy'
AND
#data.value('(/A/C)[1]', 'varchar(128)')='cat'
)
select 'record would be inserted '
ELSE select 'record was filtered'
My only other ideas:
some sort of GUID that would link records in the #filter table together, and then inner join on a GROUP BY of #filtertable, grouping by the GUID and using the SUM to match the number of records.
use semicolons to split the #filter rows, then use a CTE or something to fake a hierarchy and work from there.
Code changes made by Mikael's suggestion
CREATE TABLE #filter
(
exclusion_set SMALLINT,
exclusion_type CHAR(1) ,
excluded_value VARCHAR(10)
)
INSERT INTO #filter
VALUES (1, 'B', 'boy')
INSERT INTO #filter
VALUES (1, 'C', 'cat')
INSERT INTO #filter
VALUES (2, 'D', 'dog' )
DECLARE #data XML
SELECT #data = '<A><B>boy</B><C>cat</C></A>'
IF NOT EXISTS(
SELECT * FROM
(
select COUNT(*) AS match_count, exclusion_set
from #filter as F
where exists (
select *
from (
select X.N.value('local-name(.)', 'varchar(128)') as NodeName,
X.N.value('./text()[1]', 'varchar(max)') as NodeValue
from #data.nodes('//*') as X(N)
) T
where T.NodeName = F.exclusion_type and
T.NodeValue like F.excluded_value
)
GROUP BY exclusion_set
) matches_per_set
INNER JOIN
(SELECT COUNT(*) AS total_count, exclusion_set FROM #filter GROUP BY exclusion_set) grouped_set
ON match_count = total_count
AND grouped_set.exclusion_set = matches_per_set.exclusion_set
)
if not exists (
select *
from #filter as F
where exists (
select *
from (
select X.N.value('local-name(.)', 'varchar(128)') as NodeName,
X.N.value('./text()[1]', 'varchar(max)') as NodeValue
from #data.nodes('//*') as X(N)
) T
where T.NodeName = F.exclusion_type and
T.NodeValue like F.excluded_value
)
having count(*) = (select count(*) from #filter)
)
select 'record would be inserted '
else
select 'record was filtered'
Since I apparently get dinged if I don't mark something as the answer, I'm including mine from above. Many thanks for the help to Mikael Eriksson. His XML shred is faster than mine, and by adding the "exclusion_set" field (char(2) to make it obvious that it wasn't an IDENTITY or primary key), I can do multiple checks. If all conditions in a set match, then the record is filtered.
CREATE TABLE #filter
(
exclusion_set CHAR(2),
exclusion_type CHAR(1) ,
excluded_value VARCHAR(10)
)
INSERT INTO #filter
VALUES ('aa', 'B', 'boy')
INSERT INTO #filter
VALUES ('aa', 'C', 'cat')
INSERT INTO #filter
VALUES ('ab', 'D', 'dog' )
DECLARE #data XML
SELECT #data = '<A><B>boy</B><C>cat</C></A>'
IF NOT EXISTS(
SELECT * FROM
(
select COUNT(*) AS match_count, exclusion_set
from #filter as F
where exists (
select *
from (
select X.N.value('local-name(.)', 'varchar(128)') as NodeName,
X.N.value('./text()[1]', 'varchar(max)') as NodeValue
from #data.nodes('//*') as X(N)
) T
where T.NodeName = F.exclusion_type and
T.NodeValue like F.excluded_value
)
GROUP BY exclusion_set
) matches_per_set
INNER JOIN
(SELECT COUNT(*) AS total_count, exclusion_set FROM #filter GROUP BY exclusion_set) grouped_set
ON match_count = total_count
AND grouped_set.exclusion_set = matches_per_set.exclusion_set
)
select 'record would be inserted '
else
select 'record was filtered'

Resources