SQL correct query or not - sql

given these relationships, how could you query the following:
The tourists (name and email) that booked at least a pension whose rating is greater than 9, but didn't book any 3 star hotel with a rating less than 9.
Is the following correct?
SELECT Tourists.name, Tourists.email
FROM Tourists
WHERE EXISTS (
SELECT id FROM Bookings
INNER JOIN Tourists ON Bookings.touristId=Tourists.id
INNER JOIN AccomodationEstablishments ON Bookings.accEstId=AccomodationEstablishments.id
INNER JOIN AccomodationTypes ON AccomodationEstablishments.accType=AccomodationTypes.id
WHERE AccomodationTypes.name = 'Pension' AND
AccomodationEstablishments.rating > 9
) AND NOT EXISTS (
SELECT id FROM Bookings
INNER JOIN Tourists ON Bookings.touristId=Tourists.id
INNER JOIN AccomodationEstablishments ON Bookings.accEstId=AccomodationEstablishments.id
INNER JOIN AccomodationTypes ON AccomodationEstablishments.accType=AccomodationTypes.id
WHERE AccomodationTypes.name = 'Hotel' AND
AccomodationEstablishments.noOfStars = 3 AND
AccomodationEstablishments.rating < 9
)

I would do this using aggregation and having:
SELECT t.name, t.email
FROM Bookings b INNER JOIN
Tourists t
ON b.touristId = t.id INNER JOIN
AccomodationEstablishments ae
ON b.accEstId = ae.id INNER JOIN
AccomodationTypes a
ON ae.accType = a.id
GROUP BY t.name, t.email
HAVING SUM(CASE WHEN a.name = 'Pension' AND ae.rating > 9 THEN 1 ELSE 0 END) > 0 AND
SUM(a.name = 'Hotel' AND ae.noOfStars = 3 AND ae.rating < 9 THEN 1 ELSE 0 END)= 0;
Your method also works, but you probably need t.id in the subqueries.

Related

SQL : Percentage Completed

I need to have a SQL query to calculate the percentage of courses completed by location which are different SQL tables.
Courses table has a Status = 'C' (Completed status).
select Locations.Name, ( ??? ) as PercentCompleted
from Locations inner join Candidates ON Locations.Id = Candidates.SpecifiedLocation
inner join Courses on Candidates.Id = Courses.CandidateId
Group By Locations.Name
I want the results to be:
Location PercentCompleted
Loc1 10
Loc2 50
Loc3 75
where 10, 50 and 75 are percentages of courses completed per location.
Can this be achieved with a single SQL query?
If I understand correctly, I think you can do:
select l.Name,
avg(case when co.status = 'C' then 100.0 else 0 end) as PercentCompleted
from Locations l inner join
Candidates c
on l.Id = c.SpecifiedLocation inner join
Courses co
on c.Id = co.CandidateId
group by l.name;
try like below
select Locations.Name, (sum(case when Status = 'C' then 1 else 0 end)/(select count(*)
from Candidates c where c.SpecifiedLocation=Locations.Id))*100
as PercentCompleted
from Locations inner join Candidates ON Locations.Id = Candidates.SpecifiedLocation
inner join Courses on Candidates.Id = Courses.CandidateId
Group By Locations.Name

SQL count in select query

I have a sql query that creates a label in a cab file for a shopping company. I want to include the amount of packages in an order, some have multiple.
Each returns line in my select query has an I’d and contains a package but I need to count them.
So I have:
Name Email Weight Price ID
Joe B J#.com 10 12.5. 1
Joe B J#.com 10 12.5. 1
Joe C JC#.com 10 14.5. 2
How can I count the ID’s to return a column called pieces and in this example it would be 2 for ID 1 and 1 for ID 2
Thanks
James
enter code here
Select
'WPX' As 'Product Code',
delivery_header.dh_datetime As 'Shipment Date',
'G' As 'Shipment Type',
order_header_detail.ohd_delivery_email As 'Receiver Email Address',
variant_detail.vad_weight As 'Shipment Weight in KG',
(lots of other fields....)
delivery_header.dh_number As 'Shippers Reference',
(SELECT Count(*)
FROM delivery_header
WHERE
dh_number = OU.dh_number
) As 'Number of Pieces',
From delivery_line_item Inner Join
delivery_header On delivery_header.dh_id = delivery_line_item.dli_dh_id
Inner Join
order_line_item On delivery_line_item.dli_oli_id = order_line_item.oli_id
Inner Join
variant_detail On variant_detail.vad_id = order_line_item.oli_vad_id
Inner Join
order_header On order_header.oh_id = order_line_item.oli_oh_id Inner Join
stock_location On stock_location.sl_id = order_line_item.oli_sl_id Inner Join
customer_detail On customer_detail.cd_id = order_header.oh_cd_id Inner Join
order_header_detail On order_header.oh_id = order_header_detail.ohd_oh_id
Left Join
order_header_analysis On order_header.oh_id = order_header_analysis.oha_oh_id
Left Join
order_customer_analysis On order_header.oh_id =
order_customer_analysis.oca_oh_id Left Join
order_delivery_analysis On order_header.oh_id =
order_delivery_analysis.oda_oh_id Left Join
order_line_analysis On order_line_item.oli_id = order_line_analysis.ola_oli_id
Left Join
order_line_product_analysis On order_line_item.oli_id =
order_line_product_analysis.olpa_oli_id Left Join
order_line_variant_analysis On order_line_item.oli_id =
order_line_variant_analysis.olva_oli_id Inner Join
product_detail On product_detail.pd_id = variant_detail.vad_pd_id Inner Join
delivery_method On delivery_method.dm_id = order_header_detail.ohd_dm_id
Inner Join
delivery_method [Delivery Method] On [Delivery Method].dm_id =
order_header_detail.ohd_dm_id Inner Join
currency On currency.c_id = order_header.oh_c_id And currency.c_id =
delivery_method.dm_c_id And currency.c_id = [Delivery Method].dm_c_id
Where
delivery_header.dh_number IN (199364,199363,199362,199360)
Order By delivery_header.dh_number
You can use GROUP BY with COUNT like this:
SELECT ID, COUNT(*) as count FROM tbl GROUP BY ID
What I understood from your question is that, you are looking for output like
Name Email Weight Price ID Pieces
Joe B J#.com 10 12.5. 1 2
Joe B J#.com 10 12.5. 1 2
Joe C JC#.com 10 14.5. 2 1
Using following query you should get the desired output in most of the DBMS.
SELECT NAME,
EMAIL,
WEIGHT,
PRICE,
ID,
(SELECT Count(*)
FROM <YOUR_TABLE_NAME>
WHERE ID= OU.ID) AS Pieces
FROM <YOUR_TABLE_NAME> OU
Similar Query for MS SQL Server can be
SELECT NAME, EMAIL,WEIGHT , Price, ID,CT.Pieces
FROM <YOUR_TABLE_NAME> OU
CROSS APPLY
(
SELECT COUNT(*) AS Pieces FROM <YOUR_TABLE_NAME> IU WHERE OU.ID=IU.ID
)CT

Why doesn't my query return correct results?

I have used these Temp table to return total no of SOlved Cases and Total Number of Pending Cases from same table grouped by DIstrict e.g.
District TotalSolvedCases TotalPendingCases
A 3 1
B 8 6
C 7 1
I have done this but this doesn't return correct Result
SELECT *
INTO #Table1
FROM (
SELECT COUNT(Cases.pk_Cases_CaseID) TotalCases,
Districts.DistrictName
FROM Cases
INNER JOIN ConcernedOffices ON ConcernedOffices.pk_ConcernedOffices_ID = Cases.fk_ConcernedOffices_Cases_ConcernedOfficeID
INNER JOIN Districts ON Districts.pk_Districts_DistrictID = ConcernedOffices.fk_Districts_ConcernedOffices_DistrictID
INNER JOIN CaseHearings ON CaseHearings.fk_Cases_CaseHearings_CaseID = Cases.pk_Cases_CaseID
WHERE CaseHearings.IsClosingDate = 1
GROUP BY Districts.DistrictName
) d
SELECT *
INTO #Table2
FROM (
SELECT COUNT(Cases.pk_Cases_CaseID) TotalPedningCases,
Districts.DistrictName
FROM Cases
INNER JOIN ConcernedOffices ON ConcernedOffices.pk_ConcernedOffices_ID = Cases.fk_ConcernedOffices_Cases_ConcernedOfficeID
INNER JOIN Districts ON Districts.pk_Districts_DistrictID = ConcernedOffices.fk_Districts_ConcernedOffices_DistrictID
INNER JOIN CaseHearings ON CaseHearings.fk_Cases_CaseHearings_CaseID = Cases.pk_Cases_CaseID
WHERE CaseHearings.IsClosingDate = 0
GROUP BY Districts.DistrictName
) d
SELECT #Table1.TotalCases AS TotalSolvedCases,
#Table2.TotalPedningCases,
#Table1.DistrictName
FROM #Table1
INNER JOIN #Table2 ON #Table2.DistrictName = #Table1.DistrictName
GROUP BY #Table1.TotalCases,
#Table2.TotalPedningCases,
#Table1.DistrictName
You only need one SELECT, use case expressions to do conditional counting:
SELECT COUNT(case when CaseHearings.IsClosingDate = 1 then 1 end) TotalCases,
COUNT(case when CaseHearings.IsClosingDate = 0 then 1 end) TotalPedningCases,
Districts.DistrictName
FROM Cases
INNER JOIN ConcernedOffices ON ConcernedOffices.pk_ConcernedOffices_ID = Cases.fk_ConcernedOffices_Cases_ConcernedOfficeID
INNER JOIN Districts ON Districts.pk_Districts_DistrictID = ConcernedOffices.fk_Districts_ConcernedOffices_DistrictID
INNER JOIN CaseHearings ON CaseHearings.fk_Cases_CaseHearings_CaseID = Cases.pk_Cases_CaseID
GROUP BY Districts.DistrictName

Select only the rows where column values appear more than once

I have a select statement similar to the following:
select *
from A
inner join B on A.id_x = B.id_x
inner join C on B.id_y = C.id_y
inner join D on C.id_z = D.id_z
where
A.date > '2014-01-01'
and A.id_y = 154
and D.id_t = 2
What I want is to do something like this and count(A.id_x) > 1, which returns only the parts of the original select which repeat on A.id_x.
Is this possible?
EDIT:
I just tried to solve it using temp tables, with the code I got from T-SQL Insert into table without having to specify every column
Select * Into
#tmpBigTable
From [YourBigTable]
But I got an error message because my tables have the same column names, A.id_x and B.id_x, for example.
"Column names in each table must be unique."
Is there some way to force the issue, or declare arbitrary naming extensions?
select *
from A
inner join B on A.id_x = B.id_x
inner join C on B.id_y = C.id_y
inner join D on C.id_z = D.id_z
where
A.date > '2014-01-01'
and A.id_y = 154
and D.id_t = 2
AND A.id_x IN
(
SELECT A.id_x FROM A
GROUP BY A.id_x
HAVING count(A.id_x)>1);
You can do this with window functions:
select *
from (select *, count(*) over (partition by A.id_x) as cnt
from A inner join
B
on A.id_x = B.id_x inner join
C
on B.id_y = C.id_y inner join
D
on C.id_z = D.id_z
where A.date > '2014-01-01' and A.id_y = 154 and D.id_t = 2
) abcd
where cnt > 1;

Query for logistic regression, multiple where exists

A logistic regression is a composed of a uniquely identifying number, followed by multiple binary variables (always 1 or 0) based on whether or not a person meets certain criteria. Below I have a query that lists several of these binary conditions. With only four such criteria the query takes a little longer to run than what I would think. Is there a more efficient approach than below? Note. tblicd is a large table lookup table with text representations of 15k+ rows. The query makes no real sense, just a proof of concept. I have the proper indexes on my composite keys.
select patient.patientid
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1000
)
then '1' else '0'
end as moreThan1000
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1500
)
then '1' else '0'
end as moreThan1500
,case when exists
(
select distinct picd.patientid from patienticd as picd
inner join patient as p on p.patientid= picd.patientid
and picd.admissiondate = p.admissiondate
and picd.dischargedate = p.dischargedate
inner join tblicd as t on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%' and patient.patientid = picd.patientid
)
then '1' else '0'
end as diabetes
,case when exists
(
select r.patientid, count(*) from patient as r
where r.patientid = patient.patientid
group by r.patientid
having count(*) >1
)
then '1' else '0'
end
from patient
order by moreThan1000 desc
I would start by using subqueries in the from clause:
select q.patientid, moreThan1000, moreThan1500,
(case when d.patientid is not null then 1 else 0 end),
(case when pc.patientid is not null then 1 else 0 end)
from patient p left outer join
(select c.patientid,
(case when count(*) > 1000 then 1 else 0 end) as moreThan1000,
(case when count(*) > 1500 then 1 else 0 end) as moreThan1500
from tblclaims as c inner join
patient as p
on p.patientid=c.patientid and
c.admissiondate = p.admissiondate and
c.dischargedate = p.dischargedate
group by c.patientid
) q
on p.patientid = q.patientid left outer join
(select distinct picd.patientid
from patienticd as picd inner join
patient as p
on p.patientid= picd.patientid and
picd.admissiondate = p.admissiondate and
picd.dischargedate = p.dischargedate inner join
tblicd as t
on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%'
) d
on p.patientid = d.patientid left outer join
(select r.patientid, count(*) as cnt
from patient as r
group by r.patientid
having count(*) >1
) pc
on p.patientid = pc.patientid
order by 2 desc
You can then probably simplify these subqueries more by combining them (for instance "p" and "pc" on the outer query can be combined into one). However, without the correlated subqueries, SQL Server should find it easier to optimize the queries.
Example of left joins as requested...
SELECT
patientid,
ISNULL(CondA.ConditionA,0) as IsConditionA,
ISNULL(CondB.ConditionB,0) as IsConditionB,
....
FROM
patient
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionA from ... where ... ) CondA
ON patient.patientid = CondA.patientID
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionB from ... where ... ) CondB
ON patient.patientid = CondB.patientID
If your Condition queries only return a maximum one row, you can simplify them down to
(SELECT patientid, 1 as ConditionA from ... where ... ) CondA