Select where in subquery from one row with CSV values - sql-server

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'

Related

How to SUM() appropriate rows in MSSQL query for one column with INNER JOIN?

I have a MSSQL query:
SELECT Artikel.ArtikelID, Artikel.K_HerstellerName AS ManufacturerName, Artikel.K_HerstellerPN AS ManufacturerPN, Artikel.ArtikelNummer AS SupplierPN, Artikel.Bezeichnung + ' - ' + Artikel.LangText AS Description,
Artikel.LetzterEK AS Price, Artikel.IstGesperrt AS Gesperrt, Lager.Verfuegbar AS Quantity, Lager.LagerPlatz AS LP
FROM Artikel INNER JOIN
Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') AND (Artikel.IstGesperrt = 'False') AND (Artikel.ArtikelNummer LIKE '%[0-9]-[0-9]%')
Here you can see how it looks like: https://ibb.co/fyOHv9
How to SUM() appropriate rows in MSSQL query for column "Quantity"?
In result all rows must be unique.
you need to group by all the columns that make each row unique, then use aggregate functions in your select, for example:
SELECT
MIN(Artikel.ArtikelID ) ArtikelID,
MIN(Artikel.K_HerstellerName) ManufacturerName,
SUM(Lager.Verfuegbar) Quantity
FROM Artikel
INNER JOIN Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') ...
GROUP BY
Artikel.ArtikelID,
Artikel.K_HerstellerName,
...

Unable concate NULL value in SQL using CONCAT, COALESCE and ISNULL

I have a query with multiple joins where I want to combine records from two columns into one. If one column is empty then I want to show one column value as result. I tried with CONCAT, COALEASE and ISNULL but no luck. What am I missing here?
My objective is, create one column which has combination of s.Script AS Original and FromAnotherTable from query. Below query runs but throws Invalid column name 'Original' and Invalid column name 'FromAnotherTable'. when I try to use CONCAT, COALEASE or ISNULL .
SQL Query:
SELECT DISTINCT
c.Name AS CallCenter,
LTRIM(RTRIM(s.Name)) Name,
d.DNIS,
s.ScriptId,
s.Script AS Original,
(
SELECT TOP 5 CCSL.Line+'; '
FROM CallCenterScriptLine CCSL
WHERE CCSL.ScriptId = s.ScriptId
ORDER BY ScriptLineId FOR XML PATH('')
) AS FromAnotherTable,
--CONCAT(s.Script, SELECT TOP 5 CCSL.Line+'; ' FROM dbo.CallCenterScriptLine ccsl WHERE ccsl.ScriptId = s.ScriptId ORDER BY ccsl.ScriptLineId xml path(''))
--CONCAT(Original, FromAnotherTable) AS Option1,
--COALESCE(Original, '') + FromAnotherTable AS Option2,
--ISNULL(Original, '') + FromAnotherTable AS Option3,,
r.UnitName AS Store,
r.UnitNumber
FROM CallCenterScript s WITH (NOLOCK)
INNER JOIN CallCenterDNIS d WITH (NOLOCK) ON d.ScriptId = s.ScriptId
INNER JOIN CallCenter c WITH (NOLOCK) ON c.Id = s.CallCenterId
INNER JOIN CallCenterDNISRestaurant ccd WITH (NOLOCK) ON ccd.CallCenterDNISId = d.CallCenterDNISId
INNER JOIN dbo.Restaurant r WITH (NOLOCK) ON r.RestaurantID = ccd.CallCenterRestaurantId
WHERE c.Id = 5
AND (1 = 1)
AND (s.IsDeleted = 0 OR s.IsDeleted IS NULL)
ORDER BY DNIS ASC;
Output:
This works:
DECLARE #Column1 VARCHAR(50) = 'Foo',
#Column2 VARCHAR(50) = NULL;
SELECT CONCAT(#Column1,#Column2);
SELECT COALESCE(#Column2, '') + #Column1
SELECT ISNULL(#Column2, '') + #Column1
So I am not sure what I am missing in my original query.
Look at row 3 in the results you are getting. In your concatenated columns (Option1, 2, 3) you are getting the first script column twice. Not the first one + the second one like you expect.
The reason is because you've aliased your subquery "script" which is the same name as another column in your query, which makes it ambiguous.
Change the alias of the subquery and the problem should go away. I'm frankly surprised your query didn't raise an error.
EDIT: You can't use a column alias in another column's definition in the same level of the query. In other words, you can't do this:
SELECT
SomeColumn AS A
, (Subquery that returns a column) AS B
, A + B --this is not allowed
FROM ...
You can either create a CTE that returns the aliased columns and then concatenate them in the main query that selects from the CTE, or you have to use the original sources of the aliases, like so:
SELECT
SomeColumn AS A
, (Subquery that returns a column) AS B
, SomeColumn + (Subquery that returns a column) --this is fine
FROM ...
I took another approach where instead on creating separate column, I used ISNULL in my subQuery which returns my desired result.
Query:
SELECT DISTINCT
c.Name AS CallCenter,
LTRIM(RTRIM(s.Name)) Name,
d.DNIS,
s.ScriptId,
s.Script AS Original,
(
SELECT TOP 5 ISNULL(CCSL.Line, '')+'; ' + ISNULL(s.Script, '')
FROM CallCenterScriptLine CCSL
WHERE CCSL.ScriptId = s.ScriptId
ORDER BY ScriptLineId FOR XML PATH('')
) AS FromAnotherTable,
r.UnitName AS Store,
r.UnitNumber
FROM CallCenterScript s WITH (NOLOCK)
INNER JOIN CallCenterDNIS d WITH (NOLOCK) ON d.ScriptId = s.ScriptId
INNER JOIN CallCenter c WITH (NOLOCK) ON c.Id = s.CallCenterId
INNER JOIN CallCenterDNISRestaurant ccd WITH (NOLOCK) ON ccd.CallCenterDNISId = d.CallCenterDNISId
INNER JOIN dbo.Restaurant r WITH (NOLOCK) ON r.RestaurantID = ccd.CallCenterRestaurantId
WHERE c.Id = 5
AND (1 = 1)
AND (s.IsDeleted = 0 OR s.IsDeleted IS NULL)
ORDER BY DNIS ASC;
Here's a simplified example using table variables.
Instead of using a subquery for a field, it uses a CROSS APPLY.
And CONCAT in combination with STUFF is used to glue the strings together.
declare #Foo table (fooID int identity(1,1) primary key, Script varchar(30));
declare #Bar table (barID int identity(1,1) primary key, fooID int, Line varchar(30));
insert into #Foo (Script) values
('Test1'),('Test2'),(NULL);
insert into #Bar (fooID, Line) values
(1,'X'),(1,'Y'),(2,NULL),(3,'X'),(3,'Y');
select
f.fooID,
f.Script,
x.Lines,
CONCAT(Script+'; ', STUFF(x.Lines,1,2,'')) as NewScript
from #Foo f
cross apply (
select '; '+b.Line
from #Bar b
where b.fooID = f.fooID
FOR XML PATH('')
) x(Lines)
Result:
fooID Script Lines NewScript
----- ------- ------- -----------
1 Test1 ; X; Y Test1; X; Y
2 Test2 NULL Test2;
3 NULL ; X; Y X; Y

How to GROUP BY an alias

I trying to find the amount of classes each of our staff is scheduled to teach, I've counted the amount of times their staffID occurs in the schedule table, and it runs perfect if I only use a staff's first Name but for whatever reason it doesn't work when I try to concatenate their first and last name, and group by the assigned alias. What am I doing wrong?
SELECT DISTINCT sf.StfFirstName + ' ' + sf.StfLastname As StaffName, COUNT(fc.StaffID)
FROM Staff sf
JOIN Faculty_Classes fc
ON sf.StaffID = fc.StaffID
join Classes cl
ON fc.ClassID = cl.ClassID
GROUP BY Sf.StaffName
Just repeat the logic used in the select clause. It may look inefficient but it isn't. Note: Omit the alias given to that logic if pasting into the group by clause.
SELECT sf.StfFirstName + ' ' + sf.StfLastname As StaffName,
COUNT(fc.StaffID)
FROM Staff sf
JOIN Faculty_Classes fc
ON sf.StaffID = fc.StaffID
join Classes cl
ON fc.ClassID = cl.ClassID
GROUP BY sf.StfFirstName + ' ' + sf.StfLastname
;
You can also try without the concatenation, it should also work.
GROUP BY sf.StfFirstName, sf.StfLastname
Aliases created in the select clause are not reusable by the group by clause because the select clause actually gets performed after the group by, in other words the sequence of writing a sql query is not the sequence of operation.
You may repeat the logic as user Used_By_Already suggested or alternatively you can use a common table expression or derived table if you please:
common table expression:
with cte_example -- with name_of_cte as (your query)
as
(
SELECT sf.StfFirstName + ' ' + sf.StfLastname As StaffName, COUNT(fc.StaffID)
FROM Staff sf
JOIN Faculty_Classes fc
ON sf.StaffID = fc.StaffID
join Classes cl
ON fc.ClassID = cl.ClassID
)
select *
from cte_example
group by StaffName
derived table:
select *
from
(
SELECT sf.StfFirstName + ' ' + sf.StfLastname As StaffName, COUNT(fc.StaffID)
FROM Staff sf
JOIN Faculty_Classes fc
ON sf.StaffID = fc.StaffID
join Classes cl
ON fc.ClassID = cl.ClassID
) t1 --note the table alias
group by StaffName

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

SQL Subquery Select Table based on Outer Query

I have a general query that looks like this:
SELECT DISTINCT pb.id, pb.last, pb.first, pb.middle, pb.sex, pb.phone, pb.type,
specialties = substring(
SELECT ('|' + cs.specialty )
FROM CertSpecialty AS cs
INNER JOIN CertSpecialtyIndex AS csi on cs.specialty = csi.specialty
WHERE cs.id = pb.id
ORDER BY cs.sequence_no
FOR XML path(''),2,500)
FROM table AS pb
WHERE etc etc etc
The issue is this:
The "type" column that I'm selecting is an integer - types 1-4.
In the subquery, see where I am querying from the table CertSpecialty right now.
What I actually need to do is, if the type field comes back as a 1 or a 3, that's the table I need to query. But if the row's result is a type 2 or 4 (i.e., an ELSE), I need to be querying the same column in the table CertSpecialtyOther.
So it'd need to look something like this (though this obv doesn't work):
SELECT DISTINCT pb.id, pb.last, pb.first, pb.middle, pb.sex, pb.phone, pb.type,
specialties =
IF type in (1,3)
substring((SELECT ('|' + cs.specialty )
FROM CertSpecialty AS cs
INNER JOIN CertSpecialtyIndex AS csi on cs.specialty = csi.specialty
WHERE cs.id = pb.id
ORDER BY cs.sequence_no
FOR XML path(''),2,500)
ELSE
substring((SELECT ('|' + cs.specialty )
FROM CertSpecialtyOther AS cs
INNER JOIN CertSpecialtyIndex AS csi on cs.specialty = csi.specialty
WHERE cs.id = pb.id
ORDER BY cs.sequence_no
FOR XML path(''),2,500)
end
FROM table AS pb
WHERE etc etc etc
Is this possible? If so, what is the correct syntax? Is there a simpler way to write it where I'm switching which table I query without completely duplicating the subquery?
Also, does anyone have a good resource they could link me for this sort of thing to learn more besides?
Thanks in advance.
Use a CTE.
;WITH cs AS
(
SELECT 'A' SpecialtyCategory, phy_key, specialty
FROM CertSpecialty
UNION ALL
SELECT 'B' SpecialtyCategory, phy_key, specialty
FROM CertSpecialtyOther
)
SELECT csi.id, cs.specialty
FROM cs
INNER JOIN CertSpecialtyIndex AS csi on cs.specialty = csi.specialty
WHERE cs.phy_key = pb.phy_key
AND cs.SpecialtyCategory = (CASE WHEN type in (1,3) THEN 'A' ELSE 'B' END)

Resources