Conditionally use WHERE clause in SQL - sql

Just see this:
SELECT clientid,clientname,startdate,enddate,age FROM clients
WHERE clientid IN (1,2,3,4,5)
AND CASE WHEN age>10 THEN enddate>'31-05-2013'
END
My question: I want the second condition enddate>'31-05-2013' only if age > 10
What's wrong in this query?

Select clientid,clientname,startdate,enddate,age from clients
where clientid in (1,2,3,4,5)
and (age <= 10 OR enddate > '31-05-2013')

I don't think you can use a case expression like this. Try this instead:
Select clientid,clientname,startdate,enddate,age from clients
where clientid in (1,2,3,4,5)
and (age<=10 or enddate>'31-05-2013')

There are a couple of things that could be wrong. One is that dates are normally in 2013-05-31 order. That might change depending on locale settings; let us assume so.
Otherwise, you need to write the query more simply as:
SELECT clientid, clientname, startdate, enddate, age
FROM clients
WHERE clientid IN (1,2,3,4,5)
AND ((age > 10 AND enddate > '31-05-2013') OR (age <= 10))
Or, using the case:
SELECT clientid, clientname, startdate, enddate, age
FROM clients
WHERE clientid IN (1,2,3,4,5)
AND CASE
WHEN age > 10 THEN enddate > '31-05-2013'
ELSE TRUE
END
(The default for no ELSE clause is NULL.)

Select clientid,clientname,startdate,enddate,age from clients
where clientid in (1,2,3,4,5)
and ( (age>10 and enddate>'31-05-2013') or (age<=10 and enddate<='31-05-2013') )

I would use unambigious dateformat
Select clientid,clientname,startdate,enddate,age from clients
where clientid in (1,2,3,4,5)
and (age <= 10 OR enddate > '20130531')
See this for reason http://beyondrelational.com/modules/2/blogs/70/posts/10899/understanding-datetime-column-part-ii.aspx

Related

SQL Rowwise comparison between groups

Question
The following is a snippet of my data:
Create Table Emps(person VARCHAR(50), started DATE, stopped DATE);
Insert Into Emps Values
('p1','2015-10-10','2016-10-10'),
('p1','2016-10-11','2017-10-11'),
('p1','2017-10-12','2018-10-13'),
('p2','2019-11-13','2019-11-13'),
('p2','2019-11-14','2020-10-14'),
('p3','2020-07-15','2021-08-15'),
('p3','2021-08-16','2022-08-16');
db<>fiddle.
I want to use T-SQL to get a count of how many persons fulfil the following criteria at least once - multiples should also count as one:
For a person:
One of the dates in 'started' (say s1) is larger than at least one of the dates in 'ended' (say e1)
s1 and e1 are in the same year, to be set manually - e.g. '2021-01-01' until '2022-01-01'
Example expected response
If I put the date range '2016-01-01' until '2017-01-01' somewhere in a WHERE / HAVING clause, the output should be 1 as only p1 has both a start date and an end date that fall in 2016 where the start date is larger than the end date:
s1 = '2016-10-11', and e1 = '2016-10-10'.
Why can't I do this myself
The reason I'm stuck is that I don't know how to do this rowwise comparison between groups. The question requires comparing values across columns (start with end) across rows, within a person ID.
Use conditional aggregation to get the maximum start date and the minimum stop date in the given range.
select person
from emps
group by person
having max(case when started >= '2016-01-01' and started < '2017-01-01'
then started end) >
min(case when stopped >= '2016-01-01' and stopped < '2017-01-01'
then stopped end);
Demo: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=45adb153fcac9ce72708f1283cac7833
I would choose to use a self-outer-join with an exists correlation, it should be pretty much the most performant, all things being equal.
select Count(*)
from emps e
where exists (
select * from emps e2
where e2.person = e.person
and e2.stopped > e.started
and e.started between '20160101' and '20170101'
and e2.started between '20160101' and '20170101'
);
You said you plan to set the dates manually, so this works where we set the start date in one CTE, and the end date in another CTE. Then we calculate the min/max for each, and use that criteria in the query where statement.
with min_max_start as (
select person,
min(started) as min_start, --obsolete
max(started) as max_start
from emps
where started >= '2016-01-01'
group by person
),
min_max_end as (
select person,
min(stopped) as min_stop,
max(stopped) as max_stop --obsolete
from emps
where stopped < '2017-01-01'
group by person
)
select count(distinct e.person)
from emps e
join min_max_start mms
on e.person = mms.person
join min_max_end mme
on e.person = mme.person
where mms.max_start> mme.min_stop
Output: 1
Try the following:
With CTE as
(
Select D.person, D.started, T.stopped,
case
when Year(D.started) = Year(T.stopped) and D.started > T.stopped
then 1
else 0
end as chk
From
(Select person, started From Emps Where started >= '2016-01-01') D
Join
(Select person, stopped From Emps Where stopped <= '2017-01-01') T
On D.person = T.person
)
Select Count(Distinct person) as CNT
From CTE
Where chk = 1;
To get the employee list who met the criteria use the following on the CTE instead of the above Select Count... query:
Select person, started, stopped
From CTE
Where chk = 1;
See a demo from db<>fiddle.

Adding an aggregate condition to get total count of sub-group

Thanks for the help on this matter, I'm new with SQL. I'm trying to get a sub-count of Jedi who had more than 2 padawans last month. I tried putting the condition in WHERE but I get an error saying I can't include aggregates in it. I also tried using a CASE but kept getting a syntax error there too. Any help on this would be incredible. Thank you so much!
SELECT COUNT(DISTINCT old_republic.jedi_id), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30' AND COUNT(old_republic.padawan)>2
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
SELECT old_republic.jedi_id CASE (
WHEN Count(old_republic.padawan)>2
THEN 1
ELSE 0 End), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30'
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
I can't comment to ask for a fiddle, but from what you've written, you're probably looking for the HAVING clause.
Assuming that padawan denotes the number of Padawans:
SELECT region_id, jedi_id, sum(padawan)
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id
HAVING sum(padawan) > 2;
This query will return the sum of Padawans for each Jedi per region who had more than two Padawans last month in one region (if you don't want to take the region into account, remove it from the SELECT and GROUP BY clause). Other Jedis won't appear in the result.
You can use the CASE expression, too, in order to indicate whether a Jedi had more than two padawans:
SELECT region_id, jedi_id,
CASE WHEN sum(padawan) > 2 THEN 1 ELSE 0 END AS more_than_2_padawans
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id;
I'm not entirely sure without sample data. But I think using the HAVING clause could solve your question.
SELECT COUNT(jedi_id) as jedi_id, region_id FROM tableA
WHERE republic_date between '2022-05-20' and '2022-05-25'
GROUP BY region_id
having padawan > 2
db fiddle

How to convert 2 rows as single row result in SQL Server?

I have a table as like below
and I am expecting a output like below
I tried this query:
select
Emp1.EmployeeCode, Emp2.DateStart, Emp1.CompanyCode, Emp1.DepartmentCode
from
(select
EmployeeCode, CompanyCode, DepartmentCode,
datepart(year, DateStart) as StartDate
from
TblEmployeeDetail
group by
EmployeeCode, CompanyCode, DepartmentCode, datepart(year,DateStart)
having
count(*) > 1) as Emp1
join
TblEmployeeDetail Emp2 on Emp1.CompanyCode = Emp2.CompanyCode
and Emp1.DepartmentCode = Emp2.DepartmentCode
and Emp1.EmployeeCode = Emp2.EmployeeCode
and Emp1.StartDate = datepart(year, Emp2.DateStart)
Need help. Thank you
You can try below way - use min() and max() to get two separate date
select EmployeeCode,CompanyCode,DepartmentCode,min(datestart) as datestart1,max(datestart) as datestart2,
from TblEmployeeDetail
group by EmployeeCode,CompanyCode,DepartmentCode,DATEPART(year,DateStart)
having count(*) > 1

More than 1 appointment on the same day for a patient and display both appointments

I am trying to find patients that have more than 1 appointment on the same day. I want to then display all the appointments the patient may have. Do I need to use a subquery to do this? Here is what I have so far:
Select
Appt.ID-PatNm as Patient,
ApptNum,
Sched_ApptType.Prov.Mnemonic as Type,
Appt.Provider-Name as Provider,
Appt.Dt,
Appt.Tm,
Appt.Department-Mnemonic As Dept,
Appt.SchedulerInits,
Case $EXTRACT(Appt.InternalStatus,1)
when 'P' then 'Pending'
when 'A' then 'Arrived'
when 'R' then 'Rescheduled'
End as Status
From Sched.Appointment Appt
JOIN Sched_ApptType.Prov ON
Appt.Department = Sched_ApptType.Prov.Department
and
Appt.Provider = Sched_ApptType.Prov.Provider
and
Appt.Type = Sched_ApptType.Prov.ApptType
Where (Appt.Dt) > DATEADD('DD',-120,CURRENT_DATE)
AND Appt.InternalStatus IN ('P','R','A')
AND Appt.Department-Mnemonic= 'EYE'
Group By
Appt.ID-PatNm,
Appt.Dt
You get the patients having more than one appointment in a day by grouping by patient and day:
select distinct a.id_patnm
from sched.appointment a
group by a.id_patnm, a.dt
having count(*) > 1
So yes, you need a subquery:
Where (Appt.Dt) > DATEADD('DD',-120,CURRENT_DATE)
AND Appt.InternalStatus IN ('P','R','A')
AND Appt.Department_Mnemonic= 'EYE'
AND Appt.ID_PatNm IN
(
select a.id_patnm
from sched.appointment a
group by a.id_patnm, a.dt
having count(*) > 1
)
(BTW: I used id_patnm instead of id-patnm here, for I don't know any DBMS that would allow the hyphen. When using a hyphen in a column name you have to use quotes on the name, e.g. "id-patnm".)
I suppose you could add a column for Appointment_id which would then allow you to get the desired result.

MySql Count Query

I have this query that I am working on for a one time report. Basically what I am trying to do is find all the records where there are more than two transactions by a single account ID last month. I know it is probably something easy, my mind is just blanking.
SELECT streaming_transactions.account_id,
streaming_transactions_detail.transactions_description,
streaming_transactions_detail.transactions_detail_id,
streaming_transactions_detail.transactions_id,
streaming_transactions_detail.transactions_detail_amount,
streaming_transactions_detail.detail_type,
streaming_transactions_detail.products_id,
streaming_transactions_detail.products_levels_id,
streaming_transactions_detail.subscriptions_id,
streaming_transactions_detail.subscriptions_payment_options_id,
streaming_transactions_detail.modified
FROM streaming_transactions_detail
INNER JOIN streaming_transactions ON streaming_transactions_detail.transactions_id = streaming_transactions.transactions_id
WHERE streaming_transactions.charged = 1
AND streaming_transactions.timestamp_inserted > '2009-09-01 00:00:00'
AND streaming_transactions.account_id IN (
SELECT account_id
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
AND count(account_id) > 1)
AND streaming_transactions_detail.transactions_description LIKE '%Service Subscription%'
ORDER BY streaming_transactions.account_id DESC
I think you're almost there.
The subquery to get the transaction Ids is off, however
SELECT account_id
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
AND count(account_id) > 1)
-- Should be something like
SELECT account_id, COUNT(account_id)
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
GROUP BY account_id
HAVING count(account_id) > 1)
[unrelated] I'll throw in an unsolicited hint about style.
By using table aliases, you can improve the readabilty of the query. This can be done by optionally adding "AS xyz" where xyz is some short but mnemonic name, unique to this query, you can use xyz wherever in the query where you would use the long_named_table.
For example:
FROM streaming_transactions_detail AS D
and then
SELECT streaming_transactions.account_id,
streaming_transactions_detail.transactions_description,
streaming_transactions_detail.transactions_detail_id,
...
Can become (optionally, i.e. the "streaming_transactions_detail." still works too)
SELECT D.account_id,
D.transactions_description,
D.transactions_detail_id,
...