My Query:
select c.id, sum(case when r.paid_out>='1' then 0 else 1 end) as all_paids
from people p1, houses h, policies p2, receipts r
where p1.id = h.id
and h.code = p2.code
and p2.code_policy = r.code_policy
group by p1.id
having all_paids= 0;
Postesql return me the following error:
ERROR : There is no column 'all_paids'
I have tried several things but nothing, any help is appreciated
First, learn to use proper join syntax.
Second, you can use the expression for the having clause or a subquery:
select p1.id
from people p1 join
houses h
on p1.id = h.id join
policies p2
on h.code = p2.code join
receipts r
on p2.code_policy = r.code_policy
group by p1.id
having sum(case when r.paid_out >= '1' then 0 else 1 end) = 0;
Note: there is no c.id defined, so I assume the select should be p1.id based on the group by.
I should say that this logic would often be expressed using not exists:
select p1.*
from people p1
where not exists (select 1
from houses h
policies p2
on h.code = p2.code join
receipts r
on p2.code_policy = r.code_policy
where p1.id = h.id and
r.paid_out >= '1'
);
This eliminates the aggregation.
Related
I have written a query in SQL Server and am getting an error.
Code:
with cte as (
select pe1.name as P1,
pe2.name as P2,
m.title as T
from participant pa1
join participant pa2 on pa2.idMeeting = pa1.idMeeting
and pa2.idPerson > pa1.idPerson
join person pe1 on pe1.id = pa1.idPerson
join person pe2 on pe2.id = pa2.idPerson
join meeting m on m.id = pa1.idMeeting
),
cte_meet_max as (
select count(*) cnt, P1,P2
from cte
group by P1,P2
)
select *
from cte
where (P1,P2) in (
select P1,P2
from cte_meet_max
where cnt = (select max(cnt) from cte_meet_max)
);
Error:
Msg 4145, Level 15, State 1, Line 25
An expression of non-boolean type specified in a context where a condition is expected, near ','.
Desired result:
Can we put two columns with 'in' clause in SQL Server?
Please help.
Thanks in advance.
Note: I tried solving the question posted before question
An EXISTS condition is the proper way to rewrite this in SQL Server:
with cte as (
select pe1.name as P1,
pe2.name as P2,
m.title as T
from participant pa1
join participant pa2 on pa2.idMeeting = pa1.idMeeting
and pa2.idPerson > pa1.idPerson
join person pe1 on pe1.id = pa1.idPerson
join person pe2 on pe2.id = pa2.idPerson
join meeting m on m.id = pa1.idMeeting
),
cte_meet_max as (
select count(*) cnt, P1,P2
from cte
group by P1,P2
)
select *
from cte
where exists (
select *
from cte_meet_max m1
where m1.cnt = (select max(m2.cnt) from cte_meet_max m2)
and m1.p1 = cte.p1
and m1.p2 = cte.p2
)
Optional solution:
I used detail from both questions here.
Thanks to #Sander for the initial test case. I've made a tiny change to insert the missing meeting (id = 500), in case someone wanted to join with that table, and I've created a fiddle to show the test case:
Working Test Case for SQL Server
The solution counts the meetings each pair of participants have in common.
Additionally, I use a window function to, in the same query expression, find the MAX count over the entire set of results, avoiding one separate step.
As a result, the last query expression only needs to compare the current cnt to the max_cnt, available in each row, without needing correlated behavior.
Most are assuming the person names are unique. I also made that assumption in the first query, but used the primary keys (or would have been PKs, if constraints were created) in the second query.
WITH cte AS (
SELECT pe1.name as p1
, pe2.name as p2
, COUNT(*) AS cnt
, MAX(COUNT(*)) OVER () AS max_cnt
FROM participant pa1
JOIN participant pa2
ON pa2.idMeeting = pa1.idMeeting
AND pa2.idPerson > pa1.idPerson
JOIN person pe1 on pe1.id = pa1.idPerson
JOIN person pe2 on pe2.id = pa2.idPerson
GROUP BY pe1.name, pe2.name
)
SELECT *
FROM cte
WHERE cnt = max_cnt
;
If we don't need person names, we can avoid a little work:
WITH cte AS (
SELECT pa1.idPerson as p1
, pa2.idPerson as p2
, COUNT(*) AS cnt
, MAX(COUNT(*)) OVER () AS max_cnt
FROM participant pa1
JOIN participant pa2
ON pa2.idMeeting = pa1.idMeeting
AND pa2.idPerson > pa1.idPerson
GROUP BY pa1.idPerson, pa2.idPerson
)
SELECT *
FROM cte
WHERE cnt = max_cnt
;
You should only concatenate P1 and P2 in you're where clause
with cte as (
select pe1.name as P1,
pe2.name as P2,
m.title as T
from participant pa1
join participant pa2 on pa2.idMeeting = pa1.idMeeting
and pa2.idPerson > pa1.idPerson
join person pe1 on pe1.id = pa1.idPerson
join person pe2 on pe2.id = pa2.idPerson
join meeting m on m.id = pa1.idMeeting
),
cte_meet_max as (
select count(*) cnt, P1,P2
from cte
group by P1,P2
),
meet_max as (select P1,P2
from cte_meet_max
where cnt = (select max(cnt) from cte_meet_max))
select *
from cte ,meet_max
where cte.P1=meet_max.P1 and cte.P2=meet_max.P2
);
SELECT
p.Name,
p.Age,
MAX(COUNT(m.winTeam_ID) / (COUNT(m.winTeam_ID) + COUNT(m.lossTeam_ID)))
FROM Players AS p
INNER JOIN Teams AS t
ON t.ID = p.Team_ID
INNER JOIN Matches AS m
ON m.Team_ID = t.ID
GROUP BY
p.Name,
p.Age;
I can suggest the following query:
SELECT TOP 1
p.Name,
p.Age,
COUNT(m.winTeam_ID) / (COUNT(m.winTeam_ID) + COUNT(m.lossTeam_ID))
FROM (Players AS p
INNER JOIN Teams AS t
ON t.ID = p.Team_ID)
INNER JOIN Matches AS m
ON m.Team_ID = t.ID
GROUP BY
p.Name,
p.Age
ORDER BY
COUNT(m.winTeam_ID) / (COUNT(m.winTeam_ID) + COUNT(m.lossTeam_ID)) DESC;
This fixes the syntax problem with your joins. It also interprets the MAX as meaning that you want the record with the maximum ratio of counts. In this case, we can use TOP 1 along with ORDER BY to identify this max record.
MS Access requires strange parentheses when you have more than one join. In addition, MAX(COUNT(m.winTeam_ID)) doesn't make sense. I don't know what you are trying to calculate in the SELECT. Perhaps this does what you want:
SELECT p.Name, p.Age,
COUNT(m.winTeam_ID) / (COUNT(m.winTeam_ID) + COUNT(m.lossTeam_ID)))
FROM (Players AS p INNER JOIN
Teams AS t
ON t.ID = p.Team_ID
) INNER JOIN
Matches AS m
ON m.Team_ID = t.ID
GROUP BY p.Name, p.Age;
I think your Matches table shouldn't have a Team_ID And instead you have winTeam_ID and lossTeam_ID!
And also you want to query players of a team with - something like - the best win-rate.
If so, Use a query like this - tested on SQL Server only -:
select
p.Age, p.Name, ts.rate
from
Players p
join
(select top(1) -- sub-query will return just first record
t.ID
, sum(case when (t.ID = winTeam_ID) then 1 else 0 end) as wins
, sum(case when (t.ID = lossTeam_ID) then 1 else 0 end) as losses
, sum(case when (t.ID = winTeam_ID) then 1.0 else 0.0 end) /
(sum(case when (t.ID = winTeam_ID) then 1.0 else 0.0 end) + sum(case when (t.ID = lossTeam_ID) then 1.0 else 0.0 end)) as rate
from Teams as t
left join Matches as m
on t.ID = m.winTeam_ID
or t.ID = lossTeam_ID
group by t.ID
order by rate desc -- This will make max rate as first
) as ts -- Team stats calculated in this sub-query
on p.Team_ID = ts.ID;
I pulled person_nbrs that have never had an EventType1 before or after an EventType2. I need to pull person_nbrs that have not had an EventType1 prior to having an EventType2. If they had an EventType1 after an EventType2, than it is to be ignored. Here is my query that pulls person_nbrs that have never had an EventType1 before or after EventType2.
SELECT
person_nbr, enc_nbr, enc_timestamp
FROM
person p
JOIN
patient_encounter pe ON p.person_id = pe.person_id
JOIN
patient_procedure pp ON pe.enc_id = pp.enc_id
WHERE
enc_timestamp >= '20170101'
--EventType2
AND code_id LIKE '2'
-- EventType1
AND person_nbr NOT IN (SELECT person_nbr
FROM person p
JOIN patient_encounter pe ON p.person_id = pe.person_id
JOIN patient_procedure pp ON pe.enc_id = pp.enc_id
WHERE code_id LIKE '1')
GROUP BY
person_nbr, enc_nbr, enc_timestamp
ORDER BY
person_nbr ;
You can do this with aggregation and a HAVING clause:
SELECT p.person_nbr
FROM person p JOIN
patient_encounter pe
ON p.person_id = pe.person_id JOIN
patient_procedure pp
ON pe.enc_id = pp.enc_id
GROUP BY p.person_nbr
HAVING SUM(CASE WHEN pp.code_id = 2 THEN 1 ELSE 0 END) > 0 AND -- has code 2
(MAX(CASE WHEN pp.code_id = 1 THEN pe.timestamp END) IS NULL OR
MAX(CASE WHEN pp.code_id = 1 THEN pe.timestamp END) < MIN(CASE WHEN pp.code_id = 2 THEN pe.timestamp END)
) ;
The HAVING clause has two parts:
The first specifies that the person has a code = 2.
The second specifies one of two conditions. The first is that there is no code = 1. The second alternative is that the latest c = 1 timestamp is less than the earliest code = 2 timestamp.
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.
I want to get all unpaid male customers those who are not in any plan
SELECT cr.id, cr.type FROM mydb.customer cr
JOIN mydb.plan1 p1 on cr.id != p1.id
JOIN mydb.plan2 p2 on cr.id != p2.id
JOIN mydb.plan3 p3 on cr.id != p3.id
WHERE cr.type = 'male'
is this query correct?
You could use a series of three left joins along with IS NULL:
SELECT cr.id, cr.type
FROM mydb.customer cr
LEFT JOIN mydb.plan1 p1
ON cr.id = p1.id
LEFT JOIN mydb.plan2 p2
ON cr.id = p2.id
LEFT JOIN mydb.plan3 p3
ON cr.id = p3.id
WHERE p1.id IS NULL AND p2.id IS NULL AND p3.id iS NULL AND
cr.type = 'male'
Since all you seem to need is the id, EXCEPT should be a good choice here:
SELECT id FROM mydb.customer WHERE type = 'male'
EXCEPT ALL SELECT id FROM mydb.plan1
EXCEPT ALL SELECT id FROM mydb.plan2
EXCEPT ALL SELECT id FROM mydb.plan3;
To be precise: EXCEPT ALL:
Using EXCEPT clause in PostgreSQL
Basic techniques:
Select rows which are not present in other table
Multiple joins may not perform as fast if each table can have multiple related rows due to multiplication of rows in the intermediary derived table. Just test performance with EXPLAIN ANALYZE.