STRING_AGG gives wrong output for string values - case

I am executing a query that will fetch me the name of the doctor,the total number of patients that visited the doctor, the number of offices he went, based on the number of offices he went List out the name of the offices
The query is as follows:
SELECT DOCTOR_WORK.user_label AS "USER_LABEL",
-- COUNT(DOCTOR_WORK.WEEKEND_WORKING),
COUNT(DOCTOR_WORK.CONSULT_ID) AS "PATIENT_COUNT",
count(DISTINCT(DOCTOR_WORK.OFFICE_ID)) AS "OFFICES_DOCTOR_WORKED",
STRING_AGG(CASE
WHEN (DOCTOR_WORK.OFFICE_ID)=4 THEN 'HQ'
WHEN (DOCTOR_WORK.OFFICE_ID)=5 THEN 'TCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=6 THEN 'TCD'
WHEN (DOCTOR_WORK.OFFICE_ID)=7 THEN 'SCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=8 THEN 'SSAAC'
ELSE 'No branch assigned. Check with Admin'
END,',') AS "OFFICE_DOCTOR_WORKED_LIST"
FROM
(
SELECT
a.CONSULT_DATE,
YEAR(a.CONSULT_DATE),
MONTHNAME(a.CONSULT_DATE) AS "Month",
DAYNAME(a.entered_date)AS "Day",
CASE
WHEN a.OFFICE_ID=7 THEN 'WORKING'
WHEN a.OFFICE_ID!=7 THEN
CASE when DAYNAME(a.consult_date) in ('FRIDAY','SATURDAY') THEN 'WEEKEND' ELSE 'WORKING' END
END AS "WEEKEND_WORKING",
a.CONSULT_ID,
a.PATIENT_NAME,
a.DOCTORS_ID,
b.USER_LABEL,
a.DEPARTMENT_ID,
c.DEPARTMENT_NAME,
a.OFFICE_ID,
d.OFFICE_NAME
FROM
DOCTOR_CONSULT a
INNER JOIN VIEW_USER_SETUP b
ON (a.DOCTORS_ID=b.USER_ID)
INNER JOIN DEPARTMENT_SETUP c
ON (a.DEPARTMENT_ID=c.DEPARTMENT_ID)
INNER JOIN OFFICE_DETAILS d
ON (a.OFFICE_ID=d.OFFICE_ID)
WHERE
a.CONSULT_DATE
BETWEEN '2022-12-30' AND '2023-01-10'
AND
b.ACTIVE_STATUS='Y'
AND
d.ACTIVE_STATUS='Y'
AND
b.USER_LABEL NOT IN('Emergency Room','General Doctor','General Doctor Oph')
AND b.USER_ID=318
ORDER BY a.consult_date
) AS "DOCTOR_WORK"
WHERE
DOCTOR_WORK.WEEKEND_WORKING LIKE 'WORKING'
GROUP BY (DOCTOR_WORK.user_label);
The output that i am getting is
USER_LABEL PATIENT _COUNT OFFICE_DOCTOR_WORKED OFFICE_DOCTOR_WORKED_LIST
---------------------------------------------------------------------------------------
Doc A 98 4 SCM,SCM,TCM,TCM,.....
The above, the 4th column will display the data based on the count of patient.
The output that i require is
USER_LABEL PATIENT _COUNT OFFICE_DOCTOR_WORKED OFFICE_DOCTOR_WORKED_LIST
---------------------------------------------------------------------------------------
Doc A 98 4 SCM,TCM,SSAC,HQ
The part of the sql code that i have issue is
STRING_AGG(CASE
WHEN (DOCTOR_WORK.OFFICE_ID)=4 THEN 'HQ'
WHEN (DOCTOR_WORK.OFFICE_ID)=5 THEN 'TCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=6 THEN 'TCD'
WHEN (DOCTOR_WORK.OFFICE_ID)=7 THEN 'SCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=8 THEN 'SSAAC'
ELSE 'No branch assigned. Check with Admin'
END,',') AS "OFFICE_DOCTOR_WORKED_LIST"
What is the change that is required in the SQL statement. is WITHIN GROUP required.If so how it must be entered in the above code. This is becuase i am getting error. The error type is "Incorrect Syntax"

I was able to solve this with the following code:
SELECT DOCTOR_WORK_LIST.USER_LABEL AS "USER_LABEL",
SUM(DOCTOR_WORK_LIST.PATIENT_COUNT) AS "PATIENT_COUNT",
SUM(DOCTOR_WORK_LIST.OFFICES_DOCTOR_WORKED) AS "COUNT_OFFICES_DOCTOR_WORKED",
STRING_AGG(CASE
WHEN DOCTOR_WORK_LIST.OFFICE_DOCTOR_WORKED_LIST LIKE 'HQ' THEN 'HeadQuarters-MultiSpeciality'
WHEN DOCTOR_WORK_LIST.OFFICE_DOCTOR_WORKED_LIST LIKE 'TCM' THEN 'Tahreer Camp-MultiSpeciality'
WHEN DOCTOR_WORK_LIST.OFFICE_DOCTOR_WORKED_LIST LIKE 'TCD' THEN 'Tahreer Camp-Dental'
WHEN DOCTOR_WORK_LIST.OFFICE_DOCTOR_WORKED_LIST LIKE 'SCM' THEN 'Summod Camp-MultiSpeciality'
WHEN DOCTOR_WORK_LIST.OFFICE_DOCTOR_WORKED_LIST LIKE 'SSAAC'THEN 'Sheikh Salem Al Ali Camp'
ELSE 'No branch assigned. Check with Admin'
END,', ') AS "OFFICES_DOCTOR_WORKED_LIST"
FROM
(
SELECT DOCTOR_WORK.user_label AS "USER_LABEL",
COUNT(DOCTOR_WORK.CONSULT_ID) AS "PATIENT_COUNT",
count(DISTINCT(DOCTOR_WORK.OFFICE_ID)) AS "OFFICES_DOCTOR_WORKED",
CASE
WHEN (DOCTOR_WORK.OFFICE_ID)=4 THEN 'HQ'
WHEN (DOCTOR_WORK.OFFICE_ID)=5 THEN 'TCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=6 THEN 'TCD'
WHEN (DOCTOR_WORK.OFFICE_ID)=7 THEN 'SCM'
WHEN (DOCTOR_WORK.OFFICE_ID)=8 THEN 'SSAAC'
ELSE 'No branch assigned. Check with Admin'
END AS "OFFICE_DOCTOR_WORKED_LIST"
FROM
(
SELECT
a.CONSULT_DATE,
YEAR(a.CONSULT_DATE),
MONTHNAME(a.CONSULT_DATE) AS "Month",
DAYNAME(a.entered_date)AS "Day",
CASE
WHEN a.OFFICE_ID=7 THEN 'WORKING'
WHEN a.OFFICE_ID!=7 THEN
CASE when DAYNAME(a.consult_date) in ('FRIDAY','SATURDAY') THEN 'WEEKEND' ELSE 'WORKING' END
END AS "WEEKEND_WORKING",
a.CONSULT_ID,
a.PATIENT_NAME,
a.DOCTORS_ID,
b.USER_LABEL,
a.DEPARTMENT_ID,
c.DEPARTMENT_NAME,
a.OFFICE_ID,
d.OFFICE_NAME
FROM
DOCTOR_CONSULT a
INNER JOIN VIEW_USER_SETUP b
ON (a.DOCTORS_ID=b.USER_ID)
INNER JOIN DEPARTMENT_SETUP c
ON (a.DEPARTMENT_ID=c.DEPARTMENT_ID)
INNER JOIN OFFICE_DETAILS d
ON (a.OFFICE_ID=d.OFFICE_ID)
WHERE
a.CONSULT_DATE
BETWEEN CURRENT_DATE AND CURRENT_DATE
AND
b.ACTIVE_STATUS='Y'
AND
d.ACTIVE_STATUS='Y'
AND
b.USER_LABEL NOT IN('Emergency Room','General Doctor','General Doctor Oph')
-- AND b.USER_ID=318
ORDER BY a.consult_date
) AS "DOCTOR_WORK"
WHERE
DOCTOR_WORK.WEEKEND_WORKING LIKE 'WORKING'
GROUP BY
DOCTOR_WORK.user_label,
DOCTOR_WORK.OFFICE_ID
) AS "DOCTOR_WORK_LIST"
GROUP BY(DOCTOR_WORK_LIST.USER_LABEl);

Related

SQL: Sum based on specified dates

Thanks again for the help everyone. I went with the script below...
SELECT beginning, end,
(SELECT SUM(sale) FROM sales_log WHERE date BETWEEN beginning AND `end` ) AS sales
FROM performance
and I added a salesperson column to both the performance table and sales_log but it winds up crashing DB Browser. What is the issue here? New code below:
SELECT beginning, end, salesperson
(SELECT SUM(sale) FROM sales_log WHERE (date BETWEEN beginning AND end) AND sales_log.salesperson = performance.salesperson ) AS sales
FROM performance
I believe that the following may do what you wish or be the basis for what you wish.
WITH sales_log_cte AS
(
SELECT substr(date,(length(date) -3),4)||'-'||
CASE WHEN length(replace(substr(date,instr(date,'/')+1,2),'/','')) < 2 THEN '0' ELSE '' END
||replace(substr(date,instr(date,'/')+1,2),'/','')||'-'||
CASE WHEN length(substr(date,1,instr(date,'/') -1)) < 2 THEN '0' ELSE '' END||substr(date,1,instr(date,'/') -1) AS date,
CAST(sale AS REAL) AS sale
FROM sales_log
),
performance_cte AS
(
SELECT substr(beginning,(length(beginning) -3),4)||'-'||
CASE WHEN length(replace(substr(beginning,instr(beginning,'/')+1,2),'/','')) < 2 THEN '0' ELSE '' END
||replace(substr(beginning,instr(beginning,'/')+1,2),'/','')||'-'||
CASE WHEN length(substr(beginning,1,instr(beginning,'/') -1)) < 2 THEN '0' ELSE '' END||substr(beginning,1,instr(beginning,'/') -1)
AS beginning,
substr(`end`,(length(`end`) -3),4)||'-'||
CASE WHEN length(replace(substr(`end`,instr(`end`,'/')+1,2),'/','')) < 2 THEN '0' ELSE '' END
||replace(substr(`end`,instr(`end`,'/')+1,2),'/','')||'-'||
CASE WHEN length(substr(`end`,1,instr(`end`,'/') -1)) < 2 THEN '0' ELSE '' END||substr(`end`,1,instr(`end`,'/') -1)
AS `end`
FROM performance
)
SELECT beginning, `end` , (SELECT SUM(sale) FROM sales_log_cte WHERE date BETWEEN beginning AND `end` ) AS sales
FROM performance_cte
;
From your data this results in :-
As can be seen the bulk of the code is converting the dates into a format (i.e. YYYY-MM-DD) that is usable/recognisable by SQLite for the BETWEEN clause.
Date And Time Functions
I don't believe that you want a join between performance (preformance_cte after reformatting the dates) and sales_log (sales_log_cte) as this will be a cartesian product and then sum will sum all the results within the range.
The use of end as a column name is also awkward as it is a KEYWORD requiring it to be enclosed (` grave accents used in the above).
The above works by using 2 CTE's (Common Table Expresssions), which are temporary tables who'd life time is for the query in which they are used.
The first sales_log_cte is simply the sales_log table but with the date reformatted. The second, likewise, is simply the performace table with the dates reformatted.
If the tables already has suitable date formatting then all of the above could simply be :-
SELECT beginning, `end` , (SELECT SUM(sale) FROM sales_log WHERE date BETWEEN beginning AND `end` ) AS sales FROM performance;

Why would oracle subquery with AND & OR return returning wrogn results set

I have two subqueries. as shown below. the first query works fine but the second query which is basically the first query that I modified to use AND & OR, doesn't work in the sense that it doesn't return ID as expected. any suggestions on what is happening here?
1. (SELECT * FROM (SELECT EMPID FROM EVENT_F
INNER JOIN WCINFORMATION_D
ON EVENT_F.JOB_INFO_ROW_WID= WCINFORMATION_D.ROW_WID
INNER JOIN WCANDIDATE_D ON WCCANDIDATE_D.ROW_WID = VENT_F.CANDIDATE_ROW_WID
WHERE STEP_NAME = 'Offer'
AND WCINFORMATION_D.JOB_FAMILY_NAME IN ('MDP','ELP','Emerging Leader Program','Other')
AND TITLE NOT IN ('Student Ambassador Program for Eligible Summer Interns','Student Ambassador')
AND PI_CANDIDATE_NUM = OUTERAPP.PI_CANDIDATE_NUM
--limit 1
ORDER BY CREATION_DT ASC
) T1 WHERE ROWNUM=1) AS A_ID,
2.(SELECT * FROM (SELECT EMPID FROM EVENT_F
INNER JOIN WCINFORMATION_D
ON EVENT_F.JOB_INFO_ROW_WID= WCINFORMATION_D.ROW_WID
INNER JOIN WCANDIDATE_D ON WCCANDIDATE_D.ROW_WID = VENT_F.CANDIDATE_ROW_WID
WHERE STEP_NAME = 'Offer'
AND WCINFORMATION_D.JOB_FAMILY_NAME IN ('MDP','ELP','Emerging Leader Program','Other') or WCINFORMATION_D.JOB_FAMILY_NAME NOT IN ('MDP','ELP','Emerging Leader Program','Other')
AND TITLE NOT IN ('Student Ambassador Program for Eligible Summer Interns','Student Ambassador')
AND PI_CANDIDATE_NUM = OUTERAPP.PI_CANDIDATE_NUM
--limit 1
ORDER BY CREATION_DT ASC
) T1 WHERE ROWNUM=1) AS A_ID,
If you're wanting to get the count of people in one set of job families, plus a count of people in another set, you need to use a conditional count, e.g. something along the lines of:
SELECT COUNT(CASE WHEN wid.job_family_name IN ('MDP', 'ELP', 'Emerging Leader Program', 'Other') THEN 1 END) job_family_grp1,
COUNT(CASE WHEN wid.job_family_name IS NULL OR wid.job_family_name NOT IN ('MDP', 'ELP', 'Emerging Leader Program', 'Other') THEN 1 END) job_family_grp2
FROM event_f ef
INNER JOIN wcinformation_d wid
ON ef.job_info_row_wid = wid.row_wid
INNER JOIN wcandidate_d wcd
ON wcd.row_wid = ef.candidate_row_wid
WHERE step_name = 'Offer' -- alias this column name
AND title NOT IN ('Student Ambassador Program for Eligible Summer Interns', 'Student Ambassador') -- alias this column name;
You will most likely need to amend this to work for your particular case (it'll have to go as a join into your main query, given there are two columns being selected) since you didn't provide enough information in your question to give us the wider context.

Transpose rows to columns in SQLite

I have data like this:
I am trying to transform it to this (using SQLite). In the desired result, within each id, each start should be on the same row as the chronologically closest end. If an id has a start but no end (like id=4), then the corresponding end, will be empty (as shown below).
I have tried this
select
id,
max( case when start_end = "start" then date end) as start,
max(case when start_end = "end" then date end ) as end
from df
group by id
But the result is this, which is wrong because id=5 only have one row, when it should have two:
id start end
1 2 1994-05-01 1996-11-04
2 4 1979-07-18 <NA>
3 5 2010-10-01 2012-10-06
Any help is much appreciated
CREATE TABLE mytable(
id INTEGER NOT NULL PRIMARY KEY
,start_end VARCHAR(5) NOT NULL
,date DATE NOT NULL
);
INSERT INTO mytable(id,start_end,date) VALUES (2,'start','1994-05-01');
INSERT INTO mytable(id,start_end,date) VALUES (2,'end','1996-11-04');
INSERT INTO mytable(id,start_end,date) VALUES (4,'start','1979-07-18');
INSERT INTO mytable(id,start_end,date) VALUES (5,'start','2005-02-01');
INSERT INTO mytable(id,start_end,date) VALUES (5,'end','2009-09-17');
INSERT INTO mytable(id,start_end,date) VALUES (5,'start','2010-10-01');
INSERT INTO mytable(id,start_end,date) VALUES (5,'end','2012-10-06');
select
s.id as id,
s.date as 'start',
min(e.date) as 'end' -- earliest end date from "same id&start"
from
-- only start dates
(select id, date
from intable
where start_end='start'
) as s
left join -- keep the start-only lines
-- only end dates
(select id, date
from intable
where start_end='end'
) as e
on s.id = e.id
and s.date < e.date -- not too early
group by s.id, s.date -- "same id&start"
order by s.id, s.date; -- ensure sequence
Left join (to keep the start-only line for id "4") two on-the-fly tables, start dates and end dates.
Take the minimal end date which is just higher than start date (same id, using min()and group by.
Order by id, then start date.
I tested this on a test table which is similar to your dump, but has no "NOT NULL" and no "PRIMARY KEY". I guess for this test table that is irrelevant; otherwise explain the effect, please.
Note:
Internally three pairs of dates for id 5 (those that match end>start) are found, but only those are forwarded with the lowest end (min(end)) for each of the two different combinations of ID and start group by ID, start. The line where end>start but end not being the minimum is therefor not returned. That makes two lines with start/end pairs as desired.
Output (with .headers on):
id|start|end
2|1994-05-01|1996-11-04
4|1979-07-18|
5|2005-02-01|2009-09-17
5|2010-10-01|2012-10-06
UPDATE: Incorporate helpful comments by #MatBailie.
Thank you! This is exactly what I needed to do, only with a few changes:
SELECT
s.value AS 'url',
"AVGDATE" AS 'fieldname',
sum(e.value)/count(*) AS 'value'
FROM
(SELECT url, value
FROM quicktag
WHERE fieldname='NAME'
) AS s
LEFT JOIN
(SELECT url, substr(value,1,4) AS value
FROM quicktag
WHERE fieldname='DATE'
) AS e
ON s.url = e.url
WHERE e.value != ""
GROUP BY s.value;
I had a table like this:
url fieldname value
---------- ---------- ----------
1000052801 NAME Thomas
1000052801 DATE 2007
1000131579 NAME Morten
1000131579 DATE 2005
1000131929 NAME Tanja
1000131929 DATE 2014
1000158449 NAME Knud
1000158449 DATE 2007
1000158450 NAME Thomas
1000158450 DATE 2003
I needed to correlate NAME and DATE in columns based on url as a key, and generate a field with average DATE grouped by multiple NAME fields.
So my result looks like this:
url fieldname value
---------- ---------- ----------
Thomas AVGDATE 2005
Morten AVGDATE 2005
Tanja AVGDATE 2014
Knud AVGDATE 2007
Unfortunately I not have enough posts to make my vote count yet.

Counting 2 Columns from Separate Tables Between 2 Dates

I have a query that Counts 2 columns from 2 separate tables using subqueries, which works. Now I have to implement into this query the ability to filter out these results based on the Date of a Call Record. I will post the query in which I am working with:
SELECT (m.FirstName || " " || m.LastName) AS Members,
(
SELECT count(CallToLineOfficers.MemberID)
FROM CallToLineOfficers
WHERE CallToLineOfficers.MemberID = m.MemberID
)
+ (
SELECT count(CallToMembers.MemberID)
FROM CallToMembers
WHERE CallToMembers.MemberID = m.MemberID
) AS Tally
FROM Members AS m, Call, CallToMembers, CallToLineOfficers
Join Call on CallToMembers.CallID = Call.CallID
and CallToLineOfficers.CallID = Call.CallI
WHERE m.FirstName <> 'None'
-- and Call.Date between '2017-03-21' and '2017-03-22'
GROUP BY m.MemberID
ORDER BY m.LastName ASC;
Ok, so table Call stores the Date and its PK is CallID. Both CallToLineOfficers and CallToMembers are Bridge Tables that also contain only CallID and MemberID. With the current query, where the Date is commented out, that Date range should only return all names, but a count of 1 should appear under 1 person's name.
I have tried joining Call.CallID with both Bridge Tables' CallIDs without any luck, though I think this is the right way to do it. Could someone help point me in the right direction? I am lost. (I tried explaining this the best I could, so if you need more info, let me know.)
UPDATED: Here is a screenshot of what I am getting:
Based on the provided date in the sample, the new results, with the Date, should be:
Bob Clark - 1
Rob Catalano - 1
Matt Butler - 1
Danielle Davidson - 1
Jerry Chuska - 1
Tom Cramer - 1
Everyone else should be 0.
At the moment, the subqueries filter only on the member ID. So for any member ID in the outer query, they return the full count.
To reduce the count, you have to filter in the subqueries:
SELECT (FirstName || " " || LastName) AS Members,
(
SELECT count(*)
FROM CallToLineOfficers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
)
+ (
SELECT count(*)
FROM CallToMembers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
) AS Tally
FROM Members AS m
WHERE FirstName <> 'None'
ORDER BY LastName ASC;

Teradata case statement

Hi i am using the below query where i need to print value 'Yes' if a particular column has a value .in the below example for particular case id there are two values (one with proper date and one with NULL) so the result should be 'Yes' but i am getting two results 'Yes' and 'No'.Please find below the case statement.
select
distinct CC.CASE_ID,
case when CC.L2CALL_BK_SCEHDULED_PST_DT is not NULL
then 'Yes'
else 'No'
end as L2_OUTB_CAL_SCHD_FL
FROM EDW_KATAMARI_T.CNTCT_CASE CC
INNER JOIN EDW_KATAMARI_T.CNTCT_CASE_EXTN CCE
ON CC.CNTCT_CASE_APND_KEY = CCE.CNTCT_CASE_APND_KEY
INNER JOIN EDW_STAGE_COMN_SRC.STG_CNTCT_CASE_DELTA DELTA
on CC.CASE_ID = DELTA.CASE_ID
where
CC.CASE_ID='22922029'
group by 1,2
Moving from a comment by #dnoeth:
You need to do a MAX(CASE... and GROUP BY 1

Resources