Optimize query using Concat method - sql

I have a query which uses join, then group by caseId and then a concat-like function using STUFF.
SELECT distinct [CaseID], STUFF((SELECT ';' +space(1)+ A.[AssignedPathologist]+' ' FROM CTE1 A
WHERE A.[CaseID]=B.[CaseID] FOR XML PATH('')),1,1,'') As [AssignedPathologist]
From CTE1 B
Group By [CaseID]
The problem is that this query is super, super-slow and I tried to optimize it using CONCAT instead.
SELECT distinct A.[CaseID], [AssignedPathologist] = CASE A.AssignedPathologist = B.AssignedPathologist
WHEN 1 THEN A.AssignedPathologist
ELSE CONCAT(A.AssignedPathologist, ' ', B.AssignedPathologist)
END
FROM CTE1 A
INNER JOIN CTE1 B ON A.[CaseID]=B.[CaseID]
END
but it gives me syntax error here
[AssignedPathologist] = CASE A.AssignedPathologist = B.AssignedPathologist
which is logic because I used twice = here.
Is there any method to optimize my query using CONCAT or another methods ?
Thank you

I would try with this :
SELECT [CaseID],
STUFF( (SELECT CONCAT('; ', A.[AssignedPathologist])
FROM CTE1 A
WHERE A.[CaseID] = B.[CaseID]
FOR XML PATH('')
),1, 1, ''
) As [AssignedPathologist]
FROM (SELECT DISTINCT CaseID CTE1 B) B;
For newer versions you can use string_agg() :
SELECT CASEID, STRING_AGG(AssignedPathologist, '; ') AS AssignedPathologist
FROM CTE1 C1
GROUP BY CASEID;

Related

SELECT STUFF rewrite to Redshift

I have a SQL script, that I need to convert to redshift.
Here is the part, where I have a problem:
LEFT JOIN
(
SELECT STUFF((
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
) ,1,1,'') AS Services
) AS labelServices
I have read that I can use SELECT LISTAGG and try to use it like this:
LEFT JOIN
(
SELECT LISTAGG((
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
) ,1,1,'') AS Services
) AS labelServices
But it does not work.
So how I can rewrite it to be correct?
You don't need all the XML stuff. In fact, it is XML that is doing the aggregation in SQL Server, not STUFF(). STUFF() is just used for beautifying the string after it is created.
So, something like this:
LEFT JOIN
(SELECT cl.entityid, LISTAGG(clo.name, ', ') WITHIN GROUP (ORDER BY clo.name) as names
FROM public.label_entities cl JOIN
public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
GROUP BY cl.entityid
) AS labelServices
ON labelServices.entityid = clv.contactid
use replace() instead of stuff()
select STUFF(', hai, hello, fine', 1, 1, '')
select replace(','+', hai, hello, fine', ',,', '') --- ', hai, hello, fine' would be
--- returned by inner select
EDIT 1
select REPLACE(','+
(
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
)
,',,'
,''
)

Create view with with statement

How to create view with a with statement?
I'm getting on error on it:
WITH temp as (
select uu.email, u.logintime, u.region, p.id as panelid, p.panelname, p.numberofdownloads, dimensionType + ' (' + dimensionValue + ')' as filter
from stat_users u
left join stat_panels p
on u.id=p.sessionid
left join stat_filters f
on p.id=f.panelid
left join users uu
on uu.id=u.userid
where uu.Organization = 'name' AND
year(logintime) between 2015 and 2017
and panelname is not null
)
CREATE VIEW final as(
select aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads as downloads, case when len(aa.filters) > 0 then left(aa.filters, len(aa.filters)-1) else '' end as filters
from (
Select distinct a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
Select b.filter + ', ' AS [text()]
From temp b
Where b.panelid=a.panelid
ORDER BY b.panelid
For XML PATH ('')
) filters
from temp a
) aa
)
I'm getting such error :
> Incorrect syntax near the keyword 'CREATE'. 'CREATE VIEW' must be the
> first statement in a query batch.
So, I need just to use Create view using select which based on WITH statement on Sql server 2014
Yes always the CREATE has to be the first statement in a query batch
CREATE VIEW vFinal AS
WITH Temp AS (
SELECT uu.email, u.logintime, u.region, p.id AS panelid, p.panelname, p.numberofdownloads, dimensionType + ' (' + dimensionValue + ')' AS Filter
FROM stat_users u
LEFT JOIN stat_panels p ON u.id=p.sessionid
LEFT JOIN stat_filters f ON p.id=f.panelid
LEFT JOIN users uu ON uu.id=u.userid
WHERE uu.Organization = 'name' AND
YEAR(logintime) BETWEEN 2015 AND 2017
AND panelname IS NOT NULL
)
SELECT aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads AS downloads, CASE WHEN LEN(aa.filters) > 0 THEN LEFT(aa.filters, LEN(aa.filters)-1) else '' end as filters
FROM (
SELECT DISTINCT a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
SELECT b.filter + ', ' AS [text()]
FROM temp b
WHERE b.panelid=a.panelid
ORDER BY b.panelid
FOR XML PATH ('')
) filters
FROM temp a
) aa
GO
Syntax to create a view table using CTE
CREATE VIEW View_Name AS
WITH CTE_Name (Columns) AS (SELECT QUERY)
SELECT QUERY using the CTE Table
GO
The with clause is an optional prefix for select:
WITH query_name (column_name1, ...) AS
(SELECT ...)
SELECT ...
This is also true when with is used in a view:
CREATE VIEW ...
WITH ...
SELECT ...
;
See also: http://modern-sql.com/feature/with
CREATE or replace VIEW final as
select aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads as downloads, case when len(aa.filters) > 0 then left(aa.filters, len(aa.filters)-1) else '' end as filters
from (
Select distinct a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
Select b.filter + ', ' AS [text()]
From temp b
Where b.panelid=a.panelid
ORDER BY b.panelid
For XML PATH ('')
) filters
from temp a )

The multi-part identifier could not be bound - stuff cmd

I'm attempting a Stuff Cmd to combine multiple rows to a single entry. I keep getting "The multi-part identifier "SPCLT.CD_VAL_DESC" could not be bound." (under the first SELECT statement)
STUFF(
(SELECT
**',' + SPCLT.CD_VAL_DESC**
FROM
(
SELECT DISTINCT
SPCLT.CD_VAL_DESC SPECIALTY
FROM PIN_STATUS PS
INNER JOIN PROV_TYPE_SPCLT SPC
ON PS.PROV_ID = SPC.PROV_ID
AND SPC.VLDT_IND = 'Y'
INNER JOIN CODE_REF SPCLT
ON SPC.SPCLT_CD = SPCLT.CD_VAL
AND SPCLT.CD_REF_NM = 'SPECIALTY'
AND SPCLT.VLDT_IND = 'Y'
WHERE SPC.VLDT_IND = 'Y'
) SPCLTY
for xml
path('')
)
,1,1,'') SPECIALTIES
You need to pay attention to the format of your SQL, and then the answer would probably jump out and bite you on the nose... happens to everyone.
Your query:
STUFF(
(
SELECT
',' + SPCLT.CD_VAL_DESC
FROM
(
SELECT DISTINCT
SPCLT.CD_VAL_DESC SPECIALTY
FROM PIN_STATUS PS
INNER JOIN PROV_TYPE_SPCLT SPC
ON PS.PROV_ID = SPC.PROV_ID
AND SPC.VLDT_IND = 'Y'
INNER JOIN CODE_REF SPCLT
ON SPC.SPCLT_CD = SPCLT.CD_VAL
AND SPCLT.CD_REF_NM = 'SPECIALTY'
AND SPCLT.VLDT_IND = 'Y'
WHERE SPC.VLDT_IND = 'Y'
) SPCLTY
for xml path('')
)
,1,1,'') SPECIALTIES
...is divided into sub-queries. The STUFF() function is acting on the first SELECT beneath it.
That first SELECT is taking data FROM a sub-query, which has been aliased as SPCLTY. So, naturally, within that SELECT, you need to be referencing SPCLTY and not SPCLT.
Adding a bit of whitespace makes it a little clearer, I think.

how to omit null values using SQL query

I am trying to only display the rows in which there is date for Researchers.
I cannot manage to omit the rows with Null Values. I even tried this solution How to remove null rows from sql query result?..
This is my Query:
SELECT Submission.Title AS [Submission_Title], CA.Surname AS [Researchers], Submission.Status AS [Status]
FROM Submission
CROSS APPLY (SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE CONCAT (DATENAME(MONTH,[Submission].[SubmissionDate]), ' ',DATEPART (YEAR,[Submission].[SubmissionDate])) = 'October 2015'
AND Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')) AS CA (Surname)
GROUP BY convert(varchar(10),datename(month,Submission.SubmissionDate)), Submission.Title, CA.Surname, Submission.Status;
This is my Current output:
any suggestion. Thank you
Quickfix, without reading query:
WITH cte AS
(
SELECT Submission.Title AS [Submission_Title], CA.Surname AS [Researchers], Submission.Status AS [Status]
FROM Submission
CROSS APPLY (SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE CONCAT (DATENAME(MONTH,[Submission].[SubmissionDate]), ' ',DATEPART (YEAR,[Submission].[SubmissionDate])) = 'October 2015'
AND Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')) AS CA (Surname)
GROUP BY convert(varchar(10),datename(month,Submission.SubmissionDate)), Submission.Title, CA.Surname, Submission.Status
)
SELECT *
FROM cte
WHERE Researchers IS NOT NULL;
There is probably more elegant solution, but you need to share sample data and structures.
This part may cause problems:
SELECT DISTINCT ', ' + r.Surname
try with CONCAT instead or :
SELECT DISTINCT ', ' + ISNULL(r.Surname, '')
You should filter out the researchers before the group by rather than afterwards. When possible, it is better (performance-wise) to put conditions before aggregation.
SELECT s.Title AS Submission_Title, CA.Surname AS Researchers, s.Status
FROM Submission s CROSS APPLY
(SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN
Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE s.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'
), 1, 2, ' '))
) AS CA(Surname)
WHERE s.SubmissionDate >= '2015-10-01' AND s.SubmissionDate < '2015-11-01' AND
ca.Surname IS NULL
GROUP BY YEAR(s.SubmissionDate), MONTH(s.SubmissionDate), s.Title, CA.Surname, s.Status;
Note the changes made:
Table aliases make the query easier to write and to read.
I changed the date comparison to have no functions on the date itself. This would allow SQL Server to use an index, if appropriate.
I also moved the date comparison from the CROSS APPLY subquery to the outer query. This could be a big gain in efficiency. Why do the extra work for rows that will be filtered out anyway?
I added the NOT NULL condition to the WHERE clause.
The date key in the outer GROUP BY is redundant because the query is only using one month of data. I simplified the logic but left it.

Why does this NOT IN query work as intended, but not this NOT EXISTS query?

Working (NOT IN) retrieves 3 rows:
select DISTINCT d.* from Device d , Company c3
WHERE d.deviceid NOT IN
(
Select d1.deviceid from Device d1, Clone x1
WHERE d1.deviceid = x1.deviceID
AND
(
x1.XPath = 'hi'
OR x1.XPath = 'bye'
)
AND
(
EXISTS ( select * from (SELECT * FROM [dbo].[Split] ('T130SF0W2050', ',')) as s
WHERE x1.Value like '%' + s.items + '%' )
)
)
AND
d.companyid = c3.companyid and c3.companynumber in (SELECT * FROM [dbo].[Split] ('00223200', ','))
Not Working(not exists):
select DISTINCT d.* from Device d , Company c3
WHERE NOT EXISTS
(Select * from Device d1, Clone x1
WHERE d1.deviceid = x1.deviceID
AND
(
x1.XPath = 'hi'
OR x1.XPath = 'bye'
)
AND
(
EXISTS ( select * from (SELECT * FROM [dbo].[Split] ('T130SF0W2050', ',')) as s
WHERE x1.Value like '%' + s.items + '%' )
)
)
AND
d.companyid = c3.companyid and c3.companynumber in (SELECT * FROM [dbo].[Split] ('00223200', ','))
I'm unsure I'm using the exists syntax correct, what should I select from the subquery? I've tried a few different combinations. It won't run if I put WHERE d.deviceid NOT EXISTS
Solution (thanks to Nikola):
add AND d1.deviceid = d.deviceid inside the Exists subquery.
The difference is that the NOT IN query returns devices that match the company and don't match the inner query specification.
For the NOT EXIST query to work as written (where "work as written" refers to returning the same result as the top query), there can't be any devices that exist matching the inner query. If any devices match the inner query at all, the query won't return any results.