Embedding a subquery in a subquery - sql

I'm trying to combine these queries by embedding one in the other:
SELECT Fall.SamplePointID, SoilProperties.pH
FROM
(SELECT SamplePointID, Season, Nz (Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = "fall") AS Fall INNER JOIN SoilProperties ON Fall.SamplePointID = SoilProperties.SamplePointID;
SELECT Fall.SamplePointID
FROM (SELECT SamplePointID, Season, Nz (Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = "spring") AS Spring INNER JOIN (SELECT SamplePointID, Season, Nz (Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = "fall") AS Fall ON Spring.SamplePointID = Fall.SamplePointID
WHERE Fall.D> Spring.D;
I've tried combining them in ways like this, but then I get an error saying that it cannot find the table "Fall"
SELECT Fall.SamplePointID, SoilProperties.pH
FROM Fall INNER JOIN SoilProperties ON Fall.SamplePointID = SoilProperties.SamplePointID, (SELECT SamplePointID, Season, Nz (Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = "spring") AS Spring INNER JOIN (SELECT SamplePointID, Season, Nz (Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = "fall") AS Fall ON Spring.SamplePointID = Fall.SamplePointID
WHERE Fall.D> Spring.D;

Since the Fall derived table is the common data source, simply add Spring as a second INNER JOIN to first query. Remember too unlike other SQL dialects, MS Access requires parenthesis pairings with more than one join.
SELECT Fall.SamplePointID, sp.pH
FROM ((
SELECT SamplePointID, Season, Nz(Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = 'fall'
) AS Fall
INNER JOIN SoilProperties sp
ON Fall.SamplePointID = sp.SamplePointID)
INNER JOIN (
SELECT SamplePointID, Season, Nz(Diameter, 0) AS D
FROM SeedlingStatus
WHERE Season = 'spring'
) AS Spring
ON Fall.SamplePointID = Spring.SamplePointID
WHERE Fall.D > Spring.D
Hopefully, the Microsoft team will upgrade Access' current SQL dialect in future release including adding CTEs per my feedback ticket!

Related

sql subquery join group by

I am trying to get a list of our users from our database along with the number of people from the same cohort as them - which in this case is defined as being from the same medical school at the same time.
medical_school_id is stored in the doctor_record table
graduation_dt is stored in the doctor_record table as well.
I have managed to write this query out using a subquery which does a select statement counting the number of others for each row but this takes forever. My logic is telling me that I ought to run a simple GROUP BY query once first and then somehow JOIN the medical_school_id on to that.
The group by query is as follows
select count(ca.id) , cdr.medical_school_id, cdr.graduation_dt
from account ca
LEFT JOIN doctor cd on ca.id = cd.account_id
LEFT JOIN doctor_record cdr on cd.gmc_number = cdr.gmc_number
GROUP BY cdr.medical_school_id, cdr.graduation_dt
The long select query is
select a.id, a.email , dr.medical_school_id,
(select count(ba.id) from account ba
LEFT JOIN doctor bd on ba.id = bd.account_id
LEFT JOIN doctor_record bdr on bd.gmc_number = bdr.gmc_number
WHERE bdr.medical_school_id = dr.medical_school_id AND bdr.graduation_dt = dr.graduation_dt) AS med_count,
from account a
LEFT JOIN doctor d on a.id = d.account_id
LEFT JOIN doctor_record dr on d.gmc_number = dr.gmc_number
If you could push me in the right direction that would be amazing
I think you just want window functions:
select a.id, a.email, dr.medical_school_id, dr.graduation_dt,
count(*) over (partition by dr.medical_school_id, dr.graduation_dt) as cohort_size
from account a left join
doctor d
on a.id = d.account_id left join
doctor_record dr
on d.gmc_number = dr.gmc_number;
Using your same code for group by:
SELECT * FROM (
(
SELECT acc.[id]
, acc.[email]
FROM
account acc
LEFT JOIN
doctor doc
ON
acc.id = doc.account_id
LEFT JOIN
doctor_record doc_rec
ON
doc.gmc_number = doc_rec.gmc_number
) label
LEFT JOIN
(
SELECT count(acco.id)
, doc_reco.medical_school_id
, doc_reco.graduation_dt
FROM
account acco
LEFT JOIN
doctor doct
ON
acco.id = doct.account_id
LEFT JOIN
doctor_record doc_reco
ON
doct.gmc_number = doc_reco.gmc_number
GROUP BY
doc_reco.medical_school_id,
doc_reco.graduation_dt
) count
ON
count.[medical_school_id]=label.[medical_school_id]
AND
count.[graduation_dt]=label.[graduation_date]
)
how about something like this?
select a.doctor_id
, count(*) - 1
from doctor_record a
left join doctor_record b on a.medical_school_id = b.medical_school_id
and a.graduation_dt = b.graduation_dt
group by a.doctor_id
Subtract 1 from the count so that you're not counting the doctor in the "other folks in same cohort" number
I'm defining "same cohort" as "same medical school & graduation date".
I'm unclear on what GMC number is and how it is related. Is it something to do with cohort?

How to construct a SQL sub query in SQL Server 2008?

I have requirement to extract total number of rows from a table - ci_periodicBillings only for clients where they have rows from a particular date range from another table - ci_invoiceHeaders. I am using MS SQL Server 2008, connecting via ODBC.
I have created a subquery which works but only if the total number of rows from ci_periodicBillings is 1. I'm finding if there is more than 1 result from ci_periodicBillings, it's multiplying the rows found by the number of rows meeting the criteria from ci_invoiceHeaders.
I only want to show only the rows from ci_periodicBillings without any multiplication if the criteria is met in ci_invoiceHeaders. I'm sure there is an easy solution to this but I can't see the wood from the trees at the moment.
There are a few other tables used for listing purposes only (i.e. facilities/clients etc)
SQL is here:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT
b.name,
b.forename,
b.surname,
a.client,
cast(a.BILLSTART as DATE) as BILLSTART,
cast(a.ENDBILL as DATE) as ENDBILL,
a.RATE
FROM ci_periodicBillings as a
inner join
(select f.name,
c.surname,c.forename,ih.client,ih.invoiceDate
FROM ci_invoiceHeaders ih
LEFT JOIN ci_invoiceDetails id ON ih.invoiceNo = id.id
INNER JOIn cs_clients c ON ih.client = c.guid
INNER JOIN cs_facilities f ON c.facility = f.guid
group by f.name, c.surname,
c.forename, ih.client, ih.invoiceDate)
as b
on a.client = b.client
WHERE b.invoiceDate between '2017-08-01' and '2018-01-31'
order by a.client
Any ideas please?
Try this:
SELECT b.name, b.forename, b.surname, a.client,
cast(a.BILLSTART AS DATE) AS BILLSTART,
cast(a.ENDBILL AS DATE) AS ENDBILL, a.RATE
FROM ci_periodicBillings AS a inner join
(SELECT f.name, c.surname,c.forename,ih.client,DATE(ih.invoiceDate) invoiceDate
FROM ci_invoiceHeaders ih
LEFT JOIN ci_invoiceDetails id ON ih.invoiceNo = id.id
INNER JOIn cs_clients c ON ih.client = c.guid
INNER JOIN cs_facilities f ON c.facility = f.guid
WHERE ih.invoiceDate BETWEEN '2017-08-01' AND '2018-01-31'
GROUP BY f.name, c.surname,c.forename,ih.client,DATE(ih.invoiceDate)) AS b
ON a.client = b.client
ORDER BY a.client;

Show row in a series even if the data is missing from the table

I need a SQL query to return a row for every month in years 2015 and 2016 for every company that pays dues. The resulting dataset will show which months the company didn't pay dues by a null value. The problem is that if they didn't pay dues they won't have an entry in the database so no row will appear for than month. Here is the query:
SELECT
case when n.co_id <>'' then n.co_id else n.ID end ID
,su.CONTINUOUS_SINCE
,n.COMPANY
,a.EFFECTIVE_DATE
, a.AMOUNT
FROM dbo.Name n
LEFT OUTER JOIN dbo.Activity a ON n.ID = a.ID
inner JOIN dbo.Loc_Info l ON n.ID = l.ID
inner JOIN dbo.Segment_Categories s ON l.CURRENT_SEGMENT = s.CODE
inner JOIN dbo.Subscriptions su on su.id=n.id
WHERE   a.PRODUCT_CODE='rental' and n.MEMBER_TYPE in ('rb','rl') and a.EFFECTIVE_DATE Between '2015-07-01' And GetDate() AND a.ACTIVITY_TYPE='dues'
order by case when n.co_id <>'' then n.co_id else n.ID end, EFFECTIVE_DATE asc
If the company has paid every month it works out fine but the point is to find the companies that haven't paid so suppose Company XYZ paid every month in 2015 except June I need a row for June for Company XYZ that has a NULL value or a zero or some other indicator that they missed a payment. As it stands now the row is simply omitted because the data isn't there and it is hard to find a missing row out of thousands or rows.
I realize it is probably a different type of join or something but I am just not getting it to work out.
You can create a dummy table for the months, left join the dbo.Activity to it, that way you'll get all the months, and then join that to dbo.Name
1) Generate all the months from 1 to 12 with a recursive cte.
2) Get all months years and companies combinations with a cross join.
3) left join on this result-set to show missing months.
with months as (select 1 mth
union all
select mth+1 from months where mth<12)
,yearmonthscompanies as (select *
from months m
cross join (select 2015 yr union all select 2016 yr) y
cross join (select distinct id,co_id,company from name) c
)
SELECT
case when ymc.co_id <>'' then ymc.co_id else ymc.ID end ID
,su.CONTINUOUS_SINCE
,ymc.COMPANY
,coalesce(a.effective_date,datefromparts(ymc.yr,ymc.mth,1)) as effective_date
,coalesce(a.AMOUNT,0) amount
FROM yearmonthscompanies ymc
LEFT JOIN dbo.Name n ON n.co_id=ymc.co_id and n.id=ymc.id and n.company=ymc.company
LEFT JOIN dbo.Activity a ON n.ID = a.ID and a.PRODUCT_CODE='rental'
and n.MEMBER_TYPE in ('rb','rl') and a.EFFECTIVE_DATE Between '2015-07-01' and GetDate()
and a.ACTIVITY_TYPE='dues'
and year(a.effective_date) = ymc.yr and month(a.effective_date) = ymc.mth
inner JOIN dbo.Loc_Info l ON n.ID = l.ID
inner JOIN dbo.Segment_Categories s ON l.CURRENT_SEGMENT = s.CODE
inner JOIN dbo.Subscriptions su on su.id=n.id
order by case when ymc.co_id <>'' then ymc.co_id else ymc.ID end
,effective_date

How to optimize SQL Server query

I am copying data from one table to another table. While copying I am doing some calculation to modify one column.
SQL Server query:
INSERT INTO rat_proj_duration_map_2
SELECT
r.*,
r.hour_val / (CASE
WHEN week_val = 1 AND
(SELECT TOP 1
hrswk
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE calwk = 2
AND r.uid = u.uid
AND yr = 2016)
> 0 THEN (SELECT TOP 1
hrswk
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE calwk = 2
AND r.uid = u.uid
AND yr = 2016)
WHEN (SELECT
hrswk
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE r.week_val = us.calwk
AND r.uid = u.uid
AND yr = 2016)
< 1 AND
(SELECT
MAX(hrswk)
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE r.uid = u.uid
AND yr = 2016)
> 0 THEN (SELECT
MAX(hrswk)
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE r.uid = u.uid
AND yr = 2016)
WHEN (SELECT
COUNT(*)
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE r.uid = u.uid
AND yr = 2016)
<= 0 THEN 1
ELSE (SELECT
hrswk
FROM UserProfileRATinterface_view us
INNER JOIN users u
ON u.username = us.username
WHERE r.week_val = us.calwk
AND r.uid = u.uid
AND yr = 2016)
END) * 100 AS percentage_val
FROM rat_proj_duration_map r
When I run this query I getting time out issue.
TCP Provider: Timeout error [258]
SQL Server is not in my hand to increase time out value.
Is it possible to optimize my SQL query?
Are you sure this query is logically correct? You have several TOP 1s without specific ORDER BY, scalar comparison of subselect without TOP (which, I assume, may return more than one row if you are using top in other subselects with same source).
And yes - this query can be optimized. You can obtain all the values you need with a single subselect statement and avoid multiple execution of same subselects for each row of rat_proj_duration_map which you are having now:
INSERT INTO rat_proj_duration_map_2
SELECT
r.*,
r.hour_val / (CASE
WHEN week_val = 1 AND us.min_hrswk_2 > 0
THEN us.min_hrswk_2
WHEN us.min_hrswk_week_val <1
AND max_hrswk > 0
THEN max_hrswk
WHEN us.cnt <= 0
THEN 1
ELSE min_hrswk_week_val
END) * 100 as percentage_val
FROM
rat_proj_duration_map r
OUTER APPLY
(
SELECT
count(*) as cnt,
MIN(CASE WHEN calcw = 2 THEN hrswk END) as min_hrswk_2,
MIN(CASE WHEN calcw = r.week_val THEN hrswk END) as min_hrswk_week_val,
MAX(hrswk) as max_hrswk
FROM UserProfileRATinterface_view us
inner join users u on u.username=us.username
WHERE r.uid=u.uid and yr=2016
) us
But I can't be sure if original logic is correct. And the idea of that case to me looks like this:
...
r.hour_val / COALESCE(NULLIF(us.min_hrswk_2, 0),
NULLIF(us.min_hrswk_week_val, 0), NULLIF(max_hrswk, 0), 1)
...
The subqueries in your case clause seem to be essentially the same. You could simplify the whole command by defining a grouped version (... where yr=2016 group by u.uid) of this subquery (preferrably as a common table expression) and then work with that. This could potentially save a lot of redundant operations.
The following might work (have not tested it):
;WITH usrall as (
SELECT u.uid ui, hrswk hw, r.week wk, us.calwk cw
FROM UserProfileRATinterface_view us
INNER JOIN users u on u.username=us.username
WHERE r.uid=u.uid and yr=2016
), usrgrp as (
SELECT ui gui, MAX(hrswk) ghw, count(*) gcnt FROM usrall group by ui
), denom as (
SELECT gui dui, COALESCE( MAX(w2.hw), MAX(wkwc.hw), MAX(gwh) ) dnm
FROM usrgrp
LEFT JOIN usrall w2 ON w2.ui=gui AND w2.cw=2 AND w2.hw>0
LEFT JOIN usrall wkcw ON wkcw.ui=gui AND wkcw.wk=wkcw.cw AND wkwc.hw<1
GROUP BY gui
)
SELECT r.*, r.hour_val / d.dnm
FROM rat_proj_duration_map r
INNER JOIN denom d ON d.dui=u.uid
Essentially I have tried (I hope it works :-/) to replace the case construct by a COALESCE() function that checks the three possible calculated values one after the other. The first non-null value is accepted.
As I said: I have not tested it. Good luck

Getting a count for the first six months and last six months of FYear

I need to count all patients who had a service in both the first six months of the year and the last six months of the the fiscal year. This is what I have come up with, but not certain that it will be completely clean.
with sd as
(
select cln.cln_urn, MAX(srv_date) As Last_D, MIN(srv_date) AS First_d
from cw_domain dmn
inner join cw_service srv on srv.srv_dmn_fk=dmn.dmn_pk
inner join cw_client cln on cln.cln_pk=srv.srv_cln_fk
inner join cw_subservice sbs on sbs.sbs_pk=srv.srv_sbs_fk
inner join cw_service_category ct on ct.srv_ct_rpk=sbs.sbs_srv_ct_rfk
WHERE srv.srv_date >= '03/01/2015'
and srv.srv_date <= '02/29/2016'
and srv_ct_rpk = '002'
group by cln_urn
having MAX(srv_date)>= MIN(srv_date)+180
)
select count (distinct cln_urn)
from sd
What is being researched in this report is the number of clients who are keeping up with their treatment. In order for our client base to be considered 'in care' they must see their doctor twice a year: once in the first half and again in the second half of the year
If You want to count how many services each client has had, You need to use count function.
select HalfOfYear
, NumOfPatients = count(Client)
from (select
cln_urn as Client
,case when datepart(q,srv_date) in (1,2) then 'FirstHalf'
when datepart(q,srv_date) in (3,4) then 'SecondHalf'
end as HalfOfYear
from cw_domain dmn
inner join cw_service srv on srv.srv_dmn_fk=dmn.dmn_pk
inner join cw_client cln on cln.cln_pk=srv.srv_cln_fk
inner join cw_subservice sbs on sbs.sbs_pk=srv.srv_sbs_fk
inner join cw_service_category ct on ct.srv_ct_rpk=sbs.sbs_srv_ct_rfk
where srv.srv_date >= '03/01/2015'
and srv.srv_date <= '02/29/2016'
and srv_ct_rpk = '002'
) as x
group by HalfOfYear
This should do the trick. Just swap out the #from and #thru with your dates.
WITH sd AS
(
SELECT
DISTINCT
cln.cln_urn,
CASE
WHEN srv.srv_date < DATEADD(MONTH, 6, #from) THEN 1
WHEN srv.srv_date >= DATEADD(MONTH, 6, #from) THEN 2
END AS 'half'
FROM
cw_domain dmn
INNER JOIN cw_service AS srv
ON srv.srv_dmn_fk = dmn.dmn_pk
INNER JOIN cw_client cln
ON cln.cln_pk = srv.srv_cln_fk
INNER JOIN cw_subservice sbs
ON sbs.sbs_pk = srv.srv_sbs_fk
INNER JOIN cw_service_category ct
ON ct.srv_ct_rpk = sbs.sbs_srv_ct_rfk
WHERE
srv.srv_date >= #from
AND srv.srv_date <= #thru
AND srv_ct_rpk = '002'
)
SELECT
COUNT(cln.cln_urn)
FROM
sd AS fh
JOIN sd AS sh
ON fh.cln_urn = sh.cln_urn
WHERE
fh.half = 1
AND sh.half = 2