Why am I getting syntax error in SQL Server 'in' clause? - sql

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
);

Related

Count with exists in SQL

Why is this query not returning the count of the results? How do I get it to show the count
SELECT COUNT (*) AS MWith
FROM member m
JOIN Channel mc ON mc.MemberID = m.id
JOIN Client c ON c.id = m.clientid
JOIN packages p ON p.id = m.packageid
WHERE Enroll > '2018'
AND EXISTS (
SELECT * FROM
activity a
WHERE a.memberid = m.id
AND a.code IN ('785', 'a599')
)
GROUP BY m.id;
OUTPUT
MWith
1
1
1
The empty set is because of the clause group by .
The workarounds are:
Remove a GROUP BY, because m.id anyway is not part of the output
Use GROUP BY ALL
An original example:
SELECT COUNT(*) AS MWith
FROM member m
JOIN Channel mc
ON mc.MemberID = m.id
JOIN Client c
ON c.id = m.clientid
JOIN packages p
ON p.id = m.packageid
WHERE Enroll > '2018'
AND EXISTS
(
SELECT *
FROM activity a
WHERE a.memberid = m.id
AND a.code IN ( '785', 'a599' )
)
-- GROUP BY m.id;
Other, simpler examples to show a difference:
-- returns an empty resultset
SELECT COUNT(*) FROM sys.databases
WHERE 1=0
GROUP BY name
-- returns: a single row with 0
SELECT COUNT(*) FROM sys.databases
WHERE 1=0
-- Another example with GROUP BY ALL
-- it returns one row per grouped value, with expected count = 0
SELECT COUNT(*) FROM sys.databases
WHERE 1=0
GROUP BY ALL name

Error in on clause comparison - Big Query

The following big query code gives the following error.
select
selected_date date,
pp.name property,
bb.bookings bb,
av.available vailable,
from
(SELECT DATE(DATE_ADD(TIMESTAMP("2017-10-01"), pos - 1, "DAY")) AS selected_date
FROM (
SELECT ROW_NUMBER() OVER() AS pos, *
FROM (FLATTEN((
SELECT SPLIT(RPAD('', 1 + DATEDIFF(TIMESTAMP(CURRENT_DATE()), TIMESTAMP("2017-10-01")), '.'),'') AS h
FROM (SELECT NULL)),h
)))) v
cross join
(select p.name name from [roomsproperties.properties] p where p.name not like '%test%' group by name) as pp
left join
(select sum(b.rooms) bookings,
p.name property,
b.checkin checkin,
b.checkout checkout
from [bookings.bookings] b
left join [roomsproperties.rooms] r on r.id = b.room_id
left join [roomsproperties.properties] p on p.id = r.property_id
where p.name not like '%test%'
and b.status not in('Rejected', 'Cancelled - By customer', 'OTP Not Varified')
group by property,checkin,checkout
) as bb on pp.name = bb.property and (v.selected_date between bb.checkin and bb.checkout)
left join
(select sum(r.quantity) available,
p.name property,
date(r.created_at) date
from [roomsproperties.rooms] r
left join [roomsproperties.properties] p on p.id = r.property_id
group by property, date
) av on pp.name = av.property and v.selected_date >= av.date
The error is,
Error: ON clause must be AND of = comparisons of one field name from each table, with all field names prefixed with table name. Consider using Standard SQL
Can any one help
You should try:
(select ...) as bb on pp.name = bb.property
WHERE v.selected_date between bb.checkin and bb.checkou
and:
(select ...) as av on pp.name = av.property
WHERE v.selected_date >= av.date

Oracle SQL Correlated subquery - Returning count(*) in some columns

I have my initial statement which is :
SELECT TEAM.ID PKEY_SRC_OBJECT,
TEAM.MODF_DAT UPDATE_DATE,
TEAM.MODF_USR UPDATED_BY,
PERSO.FIRST_NAM FISRT_NAME
FROM TEAM
LEFT OUTER JOIN PERSO ON (TEAM.ID=PERSO.TEAM_ID)
I want to calculate some "flags" and return them in my initial statement.
There are 3 flags which can be calculated like this :
1) Flag ISMASTER:
SELECT Count(*)
FROM TEAM_TEAM_REL A, TEAM B
WHERE B.PARTY_PTY_ID = A.RLTD_TEAM_ID
AND CODE = 'Double';
2) Flag ISAGENT:
SELECT Count(*)
FROM TEAM_ROL_REL A, TEAM B
WHERE B.PARTY_PTY_ID = A.TEAM_ID;
3) Flag NUMPACTS:
SELECT Count(*)
FROM TEAM_ROL_REL A,
TEAM_ROL_POL_REL B,
PERSO_POL_STA_REL C,
TEAM D
WHERE A.ROL_CD IN ('1','2')
AND A.T_ROL_REL_ID = B.P_ROL_REL_ID
AND B.P_POL_ID = C.P_POL_ID
AND C.STA_CD = 'A'
AND D.PARTY_PTY_ID = A.TEAM_ID;
To try to achieve this, I've updated my initial statement like this :
WITH ABC AS (
SELECT TEAM.ID PKEY_SRC_OBJECT,
TEAM.MODF_DAT UPDATE_DATE,
TEAM.MODF_USR UPDATED_BY,
PERSO.FIRST_NAM FISRT_NAME
FROM TEAM
LEFT OUTER JOIN PERSO ON (TEAM.ID=PERSO.TEAM_ID)
)
SELECT ABC.*, MAST.ISMASTER, AGENT.ISAGENT, PACTS.NUMPACTS FROM ABC
LEFT OUTER JOIN (
select
RLTD_TEAM_ID,
Count(RLTD_TEAM_ID) OVER (PARTITION BY RLTD_TEAM_ID) as ISMASTER
FROM TEAM_TEAM_REL
WHERE CODE = 'Double'
) MAST
ON ABC.PKEY_SRC_OBJECT = MAST.RLTD_TEAM_ID
LEFT OUTER JOIN (
select
TEAM_ID,
Count(TEAM_ID) OVER (PARTITION BY TEAM_ID) as ISAGENT
FROM TEAM_ROL_REL
) AGENT
ON ABC.PKEY_SRC_OBJECT = AGENT.TEAM_ID
LEFT OUTER JOIN (
select
TEAM_ID,
Count(TEAM_ID) OVER (PARTITION BY TEAM_ID) as NUMPACTS
FROM TEAM_ROL_REL, TEAM_ROL_POL_REL, PERSO_POL_STA_REL
WHERE TEAM_ROL_REL.ROL_CD IN ('1','2')
AND TEAM_ROL_REL.T_ROL_REL_ID = TEAM_ROL_POL_REL.P_ROL_REL_ID
AND TEAM_ROL_POL_REL.P_POL_ID = PERSO_POL_STA_REL.P_POL_ID
AND PERSO_POL_STA_REL.STA_CD = 'A'
) PACTS
ON ABC.PKEY_SRC_OBJECT = PACTS.TEAM_ID;
For the two first flags (ISMASTER and ISAGENT) I get the result in less than 1min, but for the last flag (NUMPACTS) it runs few minutes without provide any result.
I think my statement is too heavy, maybe I should do it in a totally different way.
I think you have perhaps over complicated things.
You could do this (assuming I have understood your requirements correctly) like so:
WITH ttr AS (SELECT rltd_team_id,
COUNT(*) is_master
FROM team_team_rel
AND CODE = 'Double'
GROUP BY rltd_team_id),
trr AS (SELECT team_id,
COUNT(*) is_agent
FROM team_rol_rel
GROUP BY team_id)
pacts AS (SELECT trr1.team_id,
COUNT(*) num_pacts
FROM team_rol_rel trr1
INNER JOIN team_rol_pol_rel trpr ON (trr1.t_rol_rel_id = trpr.p_rol_rel_id)
INNER JOIN perso_pol_sta_rel ppsr ON (trpr.p_pol_id = ppsr.p_pol_id
WHERE trr1.rol_cd IN ('1', '2')
AND ppsr.st_cd = 'A'
GROUP BY trr1.team_id)
SELECT t.id pkey_src_object,
t.modf_dat update_date,
t.modf_usr updated_by,
p.first_nam first_name,
ttr.is_master,
trr.is_agent,
pacts.num_pacts
FROM team t
LEFT OUTER JOIN perso p ON t.id = p.team_id
LEFT OUTER JOIN ttr ON t.party_pty_id = ttr.rltd_team_id
LEFT OUTER JOIN trr ON t.party_pty_id = trr.team_id
LEFT OUTER JOIN pacts ON t.pkey_src_object = pacts.team_id;
N.B. untested, since you didn't provide any test data.

Nested Subqueries with Group By

Having trouble with this one. Getting syntax error at the arrow. I'm using subqueries on purpose. I'm wondering if it's possible to mix 'where' and 'having'?
;with books_not_ordered as
(
select BK.book_id
from bkinfo.books BK
where BK.book_id not in
(
select OD.book_id
from bkorders.order_details OD
)
)
select AU.author_id, AU.author_name_last
from bkinfo.authors AU
where exists
(
select BAU.author_id, count(*) as NumBooks
from bkinfo.book_authors BAU
group by BAU.author_id
having count(*) > 1
==>where AU.author_id = BAU.author_id
and
BAU.book_id in
(
select BK.book_id
from bkinfo.books BK
where BK.book_id in
(
select cte.book_id
from books_not_ordered cte
)
)
)
;
go
The where clause should come before your group by and having
select BAU.author_id, count(*) as NumBooks
from bkinfo.book_authors BAU
where AU.author_id = BAU.author_id
group by BAU.author_id
having count(*) > 1
Assuming that you are trying to get "The authors of the books that are not in order_details table, but who have written more than one book". The following query should work.
SELECT AU.author_id, AU.author_name_last
FROM bkinfo.books BK
JOIN bkinfo.book_authors BAU ON BAU.author_id = BK.author_id
JOIN bkinfo.authors AU ON BAU.author_id = AU.author_id
LEFT OUTER JOIN bkorders.order_details OD ON BK.book_id = OD.book_id
WHERE OD.book_id IS NULL
GROUP BY AU.author_id, AU.author_name_last
HAVING COUNT(BK.Book_id) > 1

JOIN / LEFT JOIN conflict in SQL Server

I have a tricky query. I need to select all recent versions of 2 types of members of administrator groups. Here is the query:
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
This query will return all the administrator groups. The next step will be getting the members of these groups. Since I have 2 types of memberships (Explicit, Computed), I will have to use a LEFT JOIN to make sure that I am not excluding any rows.
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
-- The JOIN bellow can be excluded but it is here just to clarify the architecture
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN tblGroup_ComputedMember cm ON refMem.ObjectUID = cm.GroupObjectID
LEFT JOIN tblGroup_ExplicitMember em ON refMem.ObjectUID = em.GroupObjectID
The last piece in the puzzle is to get the latest version of each member. For that I will have to use JOIN to exclude older versions:
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
and
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ExplicitMember
GROUP BY ObjectID
) MostRecentEM ON MostRecentEM.MaxId = em.Id
The full query will be:
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN tblGroup_ComputedMember cm ON refMem.ObjectUID = cm.GroupObjectID
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
LEFT JOIN tblGroup_ExplicitMember em ON refMem.ObjectUID = em.GroupObjectID
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ExplicitMember
GROUP BY ObjectID
) MostRecentEM ON MostRecentEM.MaxId = em.Id
The issue is clear: The 2 JOIN to exclude old versions are also applied to the select statement and clearly no rows are returned. What would be the best solution to escape such situation and to return the intended values?
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN (
select GroupObjectID, ID, max(ID) over (partition by ObjectID) as maxID
from tblGroup_ComputedMember
) cm ON refMem.ObjectUID = cm.GroupObjectID and cm.ID = cm.maxID
LEFT JOIN (
select GroupObjectID, ID, max(ID) over (partition by ObjectID) as maxID
from tblGroup_ExplicitMember
) em ON refMem.ObjectUID = em.GroupObjectID and em.ID = em.maxID
where cm.ID = cm.MaxID
What about using LEFT join in your last two joins?
LEFT JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
And then in Where clause filter values as:
WHERE MostRecentCM.MaxId IS NOT NULL
OR
MostRecentEM.MaxId IS NOT NULL