SQL Server 2014 "for xml path" into View - sql-server

I have a query that works well on its own, putting multiple values into a single column:
select substring(
(
select '/'+ Convert(nvarchar,OE) AS 'data()'
from wpb1.dbo.OE_table
where QDBID = 605441478
for xml path('')
)
,2, 255) as list1
And I have this View that I want to put it into, which works fine on its own:
SELECT
DISTINCT wpb1.dbo.Quotes.[Quote Num], MIN(wpb1.dbo.OE_table.OE) AS [Min OE],
COUNT(wpb1.dbo.OE_table.OE) AS [Count OE]
FROM wpb1.dbo.Quotes
LEFT OUTER JOIN wpb1.dbo.OE_table
ON wpb1.dbo.Quotes.QuoteDBID = wpb1.dbo.OE_table.QDBID
GROUP BY wpb1.dbo.Quotes.[Quote Num]
But when I try it causes an error saying:
Error in WHERE clause near '('. Error in GROUP BY clause. Unable to
parse query text.
This is what I am trying:
SELECT
DISTINCT wpb1.dbo.Quotes.[Quote Num], MIN(wpb1.dbo.OE_table.OE) AS [Min OE],
COUNT(wpb1.dbo.OE_table.OE) AS [Count OE],
(
select substring(
(
select '/'+ Convert(nvarchar,OE) AS 'data()'
from wpb1.dbo.OE_table
where QDBID = 605441478
for xml path('')
),2, 255)
) as list1
FROM wpb1.dbo.Quotes LEFT OUTER JOIN wpb1.dbo.OE_table
ON wpb1.dbo.Quotes.QuoteDBID = wpb1.dbo.OE_table.QDBID
GROUP BY wpb1.dbo.Quotes.[Quote Num]
What am I doing wrong?
Using SQL Server 2014.

Related

Select where in subquery from one row with CSV values

I'm trying to do a subquery where the value returned is a CSV list of values I want to use for the SELECT WHERE IN clause
SELECT *
FROM Facility.FacilityIO f
where f.FacilityID in (
select Facilities
from FacilityListView
where FacilityID = 'PSY35'
)
FacilityListView looks like this:
FacilityID Facilities
SOL1 SOL1,
PSY35 PSY3,PSY5
SOL1W SOL1,WSR1,
I get 0 results since SQL server is looking for a dataset and not an individual value but I don't know how to tell SQL server to select where f.FacilityID in ('PSY3','PSY5')
Join the tables and use the LIKE operator:
SELECT f.*
FROM Facility.FacilityIO f INNER JOIN FacilityListView v
ON ',' + v.Facilities + ',' LIKE '%,' + f.FacilityID + ',%'
WHERE v.FacilityID = 'PSY35'

How to use alias in where clause in SQL Server

I have following query and i am getting perfect result what i want but i want to use stManufacturerPartReference alias in where clause condition Like following second query but it gives me error.
WITH ProductsCTE (inProductId, inCategoryId, stCategory, stManufacturers, inCompanyId, stERPId,stManufacturerPartReference,
stProductName, stProductNumber, stModel, stFileLink, stImage, dcPrice,dcStandardPrice, dcOnHandQty,dcQtyOnPO,dtEstimatedShipDate, dcWeight, inSyncStatus, dtLastSyncDate,
inErrorRetry,flgIsActive, flgIsDeleted, inCreatedBy, inModifiedBy, dtModificationDate, dtCreationDate, inRecordCount)
AS (
SELECT
product.inProductId,
product.inCategoryId,
product.stCategory,
product.stManufacturers,
product.inCompanyId,
product.stERPId,
product.stProductName,
STUFF((SELECT ', ' + PM.stManufacturerPartReference
FROM tblProductManufacturers PM
JOIN tblProducts Product on PM.inProductId = Product.inProductId
JOIN tblManufacturers M on M.inManufacturerId = PM.inManufacturerId
WHERE PM.inProductId=product.inProductId
ORDER BY M.stManufacturer
FOR XML PATH('')), 1, 1, '') as stManufacturerPartReference,
product.stProductNumber,
product.stModel,
product.stFileLink,
product.stImage,
product.dcPrice,
product.dcStandardPrice,
product.dcOnHandQty,
product.dcQtyOnPO,
product.dtEstimatedShipDate,
product.dcWeight,
product.inSyncStatus,
product.dtLastSyncDate,
product.inErrorRetry,
product.flgIsActive,
product.flgIsDeleted,
product.inCreatedBy,
product.inModifiedBy,
product.dtModificationDate,
product.dtCreationDate,
CAST((COUNT(product.inProductId) OVER()) AS BIGINT) AS inRecordCount
FROM tblProducts Product WITH (NOLOCK)
WHERE 1=1
AND product.flgIsDeleted <> 1
AND flgIsHistoricItem <> 1 AND (product.inCompanyId = 1) )
SELECT P.inProductId,
P.inCategoryId,
P.stCategory,
P.stManufacturers,
P.stManufacturerPartReference,
P.inCompanyId,
P.stERPId,
P.stProductName,
P.stProductNumber,
P.stModel,
P.stFileLink,
P.stImage,
P.dcPrice,
P.dcStandardPrice,
P.dcOnHandQty,
P.dcQtyOnPO,
P.dtEstimatedShipDate,
P.dcWeight,
P.inSyncStatus,
P.dtLastSyncDate,
P.inErrorRetry,
P.flgIsActive,
P.flgIsDeleted,
P.inCreatedBy,
P.inModifiedBy,
P.dtModificationDate,
P.dtCreationDate,
P.inRecordCount
FROM ProductsCTE P
ORDER BY stCategory ASC
OFFSET (1 - 1) * 1000 ROWS
FETCH NEXT 1000 ROWS ONLY;
i want to use stManufacturerPartReference in Where Clause Like Following.
WITH ProductsCTE (inProductId, inCategoryId, stCategory, stManufacturers, inCompanyId, stERPId,stManufacturerPartReference,
stProductName, stProductNumber, stModel, stFileLink, stImage, dcPrice,dcStandardPrice, dcOnHandQty,dcQtyOnPO,dtEstimatedShipDate, dcWeight, inSyncStatus, dtLastSyncDate,
inErrorRetry,flgIsActive, flgIsDeleted, inCreatedBy, inModifiedBy, dtModificationDate, dtCreationDate, inRecordCount)
AS (
SELECT
product.inProductId,
product.inCategoryId,
product.stCategory,
product.stManufacturers,
product.inCompanyId,
product.stERPId,
product.stProductName,
STUFF((SELECT ', ' + PM.stManufacturerPartReference
FROM tblProductManufacturers PM
JOIN tblProducts Product on PM.inProductId = Product.inProductId
JOIN tblManufacturers M on M.inManufacturerId = PM.inManufacturerId
WHERE PM.inProductId=product.inProductId
ORDER BY M.stManufacturer
FOR XML PATH('')), 1, 1, '') as stManufacturerPartReference,
product.stProductNumber,
product.stModel,
product.stFileLink,
product.stImage,
product.dcPrice,
product.dcStandardPrice,
product.dcOnHandQty,
product.dcQtyOnPO,
product.dtEstimatedShipDate,
product.dcWeight,
product.inSyncStatus,
product.dtLastSyncDate,
product.inErrorRetry,
product.flgIsActive,
product.flgIsDeleted,
product.inCreatedBy,
product.inModifiedBy,
product.dtModificationDate,
product.dtCreationDate,
CAST((COUNT(product.inProductId) OVER()) AS BIGINT) AS inRecordCount
FROM tblProducts Product WITH (NOLOCK)
WHERE 1=1
AND product.flgIsDeleted <> 1
AND flgIsHistoricItem <> 1 AND (product.inCompanyId = 1) AND stManufacturerPartReference LIKE '%ABC DEF%' )
SELECT P.inProductId,
P.inCategoryId,
P.stCategory,
P.stManufacturers,
P.stManufacturerPartReference,
P.inCompanyId,
P.stERPId,
P.stProductName,
P.stProductNumber,
P.stModel,
P.stFileLink,
P.stImage,
P.dcPrice,
P.dcStandardPrice,
P.dcOnHandQty,
P.dcQtyOnPO,
P.dtEstimatedShipDate,
P.dcWeight,
P.inSyncStatus,
P.dtLastSyncDate,
P.inErrorRetry,
P.flgIsActive,
P.flgIsDeleted,
P.inCreatedBy,
P.inModifiedBy,
P.dtModificationDate,
P.dtCreationDate,
P.inRecordCount
FROM ProductsCTE P
ORDER BY stCategory ASC
OFFSET (1 - 1) * 1000 ROWS
FETCH NEXT 1000 ROWS ONLY;
But it Gives me Error "Invalid column name 'stManufacturerPartReference'."
So how can i use alias in where clause please help.
thanks.
I would do instead :
SELECT *, STUFF(stManufacturerPartReference, 1, 1, '') AS stManufacturerPartReference
FROM . . . .
. . . . CROSS APPLY
( SELECT ', ' + PM.stManufacturerPartReference
FROM tblProductManufacturers PM JOIN
tblProducts Product
ON PM.inProductId = Product.inProductId JOIN
tblManufacturers M
ON M.inManufacturerId = PM.inManufacturerId
WHERE PM.inProductId=product.inProductId
FOR XML PATH('')
) tt(stManufacturerPartReference)
WHERE . . . AND
stManufacturerPartReference LIKE '%ABC DEF%';
You need to learn about the order of execution in a SQL query: https://sqlbolt.com/lesson/select_queries_order_of_execution
WHERE comes immediately after FROM, and therefore any aliases are not available to filter on.
As it's wrapped in a CTE, filter on stManufacturerPartReference in the query after it.
WITH ProductsCTE (inProductId, inCategoryId, stCategory, stManufacturers, inCompanyId, stERPId,stManufacturerPartReference,
stProductName, stProductNumber, stModel, stFileLink, stImage, dcPrice,dcStandardPrice, dcOnHandQty,dcQtyOnPO,dtEstimatedShipDate, dcWeight, inSyncStatus, dtLastSyncDate,
inErrorRetry,flgIsActive, flgIsDeleted, inCreatedBy, inModifiedBy, dtModificationDate, dtCreationDate, inRecordCount)
AS (
SELECT
product.inProductId,
product.inCategoryId,
product.stCategory,
product.stManufacturers,
product.inCompanyId,
product.stERPId,
product.stProductName,
STUFF((SELECT ', ' + PM.stManufacturerPartReference
FROM tblProductManufacturers PM
JOIN tblProducts Product on PM.inProductId = Product.inProductId
JOIN tblManufacturers M on M.inManufacturerId = PM.inManufacturerId
WHERE PM.inProductId=product.inProductId
ORDER BY M.stManufacturer
FOR XML PATH('')), 1, 1, '') as stManufacturerPartReference,
product.stProductNumber,
product.stModel,
product.stFileLink,
product.stImage,
product.dcPrice,
product.dcStandardPrice,
product.dcOnHandQty,
product.dcQtyOnPO,
product.dtEstimatedShipDate,
product.dcWeight,
product.inSyncStatus,
product.dtLastSyncDate,
product.inErrorRetry,
product.flgIsActive,
product.flgIsDeleted,
product.inCreatedBy,
product.inModifiedBy,
product.dtModificationDate,
product.dtCreationDate,
CAST((COUNT(product.inProductId) OVER()) AS BIGINT) AS inRecordCount
FROM tblProducts Product WITH (NOLOCK)
WHERE 1=1
AND product.flgIsDeleted 1
AND flgIsHistoricItem 1 AND (product.inCompanyId = 1) )
SELECT P.inProductId,
P.inCategoryId,
P.stCategory,
P.stManufacturers,
P.stManufacturerPartReference,
P.inCompanyId,
P.stERPId,
P.stProductName,
P.stProductNumber,
P.stModel,
P.stFileLink,
P.stImage,
P.dcPrice,
P.dcStandardPrice,
P.dcOnHandQty,
P.dcQtyOnPO,
P.dtEstimatedShipDate,
P.dcWeight,
P.inSyncStatus,
P.dtLastSyncDate,
P.inErrorRetry,
P.flgIsActive,
P.flgIsDeleted,
P.inCreatedBy,
P.inModifiedBy,
P.dtModificationDate,
P.dtCreationDate,
P.inRecordCount
FROM ProductsCTE P
WHERE P.stManufacturerPartReference LIKE '%ABC DEF%'
ORDER BY stCategory ASC
OFFSET (1 - 1) * 1000 ROWS
FETCH NEXT 1000 ROWS ONLY;
You have to use outer apply or cross apply (select .. AS stManufacturerPartReference) and if your server is 2017 you could use String_AGG function to get list of values instead of for xml clause.

SQL Server Query for required result

I am using SQL Server with my application.
The Table data is as following :
And I want result in following format:
I have tried with split function but its not working properly.
Is it possible to get such a result.
Please suggest.
Thank you.
Try this. I did not manage to get a single Not Req, it is like this ("Not Req/Not Req").
drop table if exists dbo.TableB;
create table dbo.TableB (
OldSPC varchar(100)
, old_freq varchar(100)
, NewSPC varchar(100)
, new_freq varchar(100)
);
insert into dbo.TableB(OldSPC, old_freq, NewSPC, new_freq)
values ('ADH,BAP', '7,7', 'ADH,BAP', '7,7')
, ('Not Req', 'Not Req', 'ADH,BAP', '7,7')
, ('BAP,EXT,ADL', '35,7,42', 'BAP,EXT,BAP,ADL', '21,7,35,42');
select
tt1.OldSPCOldFreq
, tt2.NewSPCNewFreq
from (
select
t.OldSPC, t.old_freq, t.NewSPC, t.new_freq
, STRING_AGG(t1.value + '/' + t2.value, ',') OldSPCOldFreq
from dbo.TableB t
cross apply (
select
ROW_NUMBER () over (order by t.OldSPC) as Rbr
, ss.value
from string_split (t.OldSPC, ',') ss
) t1
cross apply (
select
ROW_NUMBER () over (order by t.old_freq) as Rbr
, ss.value
from string_split (t.old_freq, ',') ss
) t2
where t1.Rbr = t2.Rbr
group by t.OldSPC, t.old_freq, t.NewSPC, t.new_freq
) tt1
inner join (
select
t.OldSPC, t.old_freq, t.NewSPC, t.new_freq
, STRING_AGG(t3.value + '/' + t4.value, ',') NewSPCNewFreq
from dbo.TableB t
cross apply (
select
ROW_NUMBER () over (order by t.NewSPC) as Rbr
, ss.value
from string_split (t.NewSPC, ',') ss
) t3
cross apply (
select
ROW_NUMBER () over (order by t.new_freq) as Rbr
, ss.value
from string_split (t.new_freq, ',') ss
) t4
where t3.Rbr = t4.Rbr
group by t.OldSPC, t.old_freq, t.NewSPC, t.new_freq
) tt2 on tt1.OldSPC = tt2.OldSPC
and tt1.old_freq = tt2.old_freq
and tt1.NewSPC = tt2.NewSPC
and tt1.new_freq = tt2.new_freq
As mentioned in comments, it might be easier for you to do it on front end, but it could be done in SQL Server as well.
Partial Rextester Demo
I didn't replicate your whole scenario but got it for 2 columns. To do it first of all, you need a unique identifier for each row. I am using a sequence number (1,2,3...).
Now refer to this answer, which uses recursive subquery to split csv to rows. Then I used XML PATH to change columns back to csv.
This is the query which is doing it for OLD SPC and OLD FREQ.
;with tmp(SEQ,OldSPCItem,OldSPC,OLD_FREQ_item,OLD_FREQ) as (
select SEQ, LEFT(OldSPC, CHARINDEX(',',OldSPC+',')-1),
STUFF(OldSPC, 1, CHARINDEX(',',OldSPC+','), ''),
LEFT(OLD_FREQ, CHARINDEX(',',OLD_FREQ+',')-1),
STUFF(OLD_FREQ, 1, CHARINDEX(',',OLD_FREQ+','), '')
from table1
union all
select SEQ, LEFT(OldSPC, CHARINDEX(',',OldSPC+',')-1),
STUFF(OldSPC, 1, CHARINDEX(',',OldSPC+','), ''),
LEFT(OLD_FREQ, CHARINDEX(',',OLD_FREQ+',')-1),
STUFF(OLD_FREQ, 1, CHARINDEX(',',OLD_FREQ+','), '')
from tmp
where OldSPC > ''
)
select seq,STUFF( (SELECT ',' + CONCAT(OldSPCItem,'/',OLD_FREQ_item) FROM TMP I
WHERE I.seq = O.seq FOR XML PATH('')),1,1,'') OLD_SPC_OLD_FREQ
from tmp O
GROUP BY seq
;
It will give you this output
+-----+------------------+
| seq | OLD_SPC_OLD_FREQ |
+-----+------------------+
| 1 | ADH/7,BAP/9 |
| 2 | NOT REQ/NOT REQ |
+-----+------------------+
What do you have to do now
- Find a way to generate a sequence number to uniquely identify each row. If you can use any column, use that instead of SEQ.
Similarly add logic for NEW_SPC and NEW_FREQ. (just copy paste LEFT and STUFF like in OLD_FREQ and change it for NEW_SPC and NEW_FREQ.
Replace multiple NOT REQ/ with '', so you will get only one NOT REQ. You can do it with replace function.
If you face any issue/error while doing so, add it to the Rexterster Demo and share the URL, we will check that.

SQL Stuff from Subquery

I have a query that returns a list of services and ContractorIDs. I need to stuff these services into a field to join them with another select statement by ContractorID, but I can't figure out how to do it.
The Select that lists the services is "
SELECT DISTINCT SM.ContractorID,
CASE WHEN S.bitRestrictedSelection = 1
THEN S.vchDescription + '*'
ELSE S.vchDescription
END AS vchDescription
FROM tblAscServiceRegionToOperator SRTO
INNER JOIN tblServiceMatrix SM
ON SRTO.OperatorID = 12624
AND SM.ServiceRegionID = SRTO.ServiceRegionID
AND SM.bitPrimaryService = 1
INNER JOIN tblServices S
ON S.ServiceID = SM.ServiceID
This produces the following:
In the example, for Contractor #16 He has 4 services I need to put them in one field called services by joining with another Select statement
I tried the following, but I get errors:
Select DISTINCT CompanyID, vchCompanyName as CompanyName,vchFIDNumber,vchPrimContactName, vchPrimContactEmail
,stuff((','
SELECT DISTINCT
SM.ContractorID,
CASE WHEN S.bitRestrictedSelection = 1
THEN S.vchDescription + '*'
ELSE S.vchDescription
END AS vchDescription
FROM tblAscServiceRegionToOperator SRTO
INNER JOIN tblServiceMatrix SM
ON SRTO.OperatorID = 12624
AND SM.ServiceRegionID = SRTO.ServiceRegionID
AND SM.bitPrimaryService = 1
INNER JOIN tblServices S
ON S.ServiceID = SM.ServiceID
FOR XML PATH('')
), 1, 1, '') as Services from tblCompany
Any assistance is greatly appreciated!!!
The following query would work:
SELECT SS.contractor Contractor,
STUFF((SELECT '; ' + US.vchdescription
FROM ServicesList US
WHERE US.contractor = SS.contractor
FOR XML PATH('')), 1, 1, '') [Services]
FROM ServicesList SS
GROUP BY SS.contractor
ORDER BY 1
I have created a table and inserted there two rows for contractor = 16, for you to get the idea.
You can use the suggestion given to you in the comments, to wrap the first select into a CTE and then perform the STUFF function on that CTE.
You can check a demo of this query here.

Create View - Declare a variable

I am creating a view that is using that STUFF function. I want to put the result of STUFF in a variable for my view. The problem I am having is declaring my variable. It gives me the message "Incorrect Syntax near 'DECLARE'. Expecting '(' or SELECT." I already have the '(' in there. I have tried putting a BEGIN before it. I have tried putting it after the SELECT word. But nothing seems to work and I cannot find a solution in my search. I am using SQL Server 2012
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
(DECLARE #CONDITIONS AS varchar(20)
SET #CONDITIONS = (SELECT DISTINCT BD.[RequestedBurnsID]
,[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20),[ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions] WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD)
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT
,#CONDITIONS AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
GO
You can't declare variables in a view. Could you make it into a function or stored procedure?
Edit - you might also be able to put something into a CTE (Common Table Expression) and keep it as a view.
e.g.
WITH conditions as
(
... do the STUFF here
)
SELECT blah
FROM blah
INNER JOIN conditions
(or CROSS JOIN conditions if its just one row, I can't quite decipher what your data is like)
Here is a sample query that uses a CTE (Common Table Expression) to nicely emulate internal variable construction, as described by James Casey. You can test-run it in your version of SQL Server.
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
yielding output:
status name
12 dbo
0 db_accessadmin
0 db_securityadmin
0 db_ddladmin
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Or use a CTE (common table expression) as subselect like:
WITH CTE_Time(Clock)
AS(
SELECT 11 AS [Clock] -- set var
)
SELECT
DATEPART(HOUR, GETDATE()) AS 'actual hour',
CASE
WHEN DATEPART(HOUR, GETDATE()) >= (SELECT [Clock] FROM CTE_Time) THEN 'after'
ELSE 'before'
END AS [Data]
Try put the condition subquery directly inside the the view select statement. you may CAST the XML to VARCHAR(20).
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT,
(
SELECT DISTINCT BD.[RequestedBurnsID],
[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20), [ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions]
WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD
) AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID

Resources