SQL WHEREing on a different table's COUNT - sql

So, I want to apply a WHERE condition to a field assigned by a COUNT() AS clause. My query currently looks like this:
SELECT new_tags.tag_id
, new_tags.tag_name
, new_tags.tag_description
, COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
AND `entry_count` < '1'
GROUP BY new_tags.tag_id ORDER BY tag_name ASC
The bit that's failing is the entry_count in the WHERE clause - it doesn't know what the entry_count column is. My table looks like this:
new_tags {
tag_id INT
tag_name VARCHAR
}
new_tags_entries {
tag_id INT
entry_id INT
}
I want to filter the results by the number of distinct entry_ids in new_tags_entries that pertain to the tag ID.
Make sense?
Thanks in advance.

To filter on aggegated values use the HAVING clause...
SELECT
new_tags.tag_id, new_tags.tag_name,
new_tags.tag_description,
COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
GROUP BY new_tags.tag_id
HAVING COUNT(DISTINCT new_tags_entries.entry_id) < '1'
ORDER BY tag_name ASC

An inner join will never have a count of less than 1. Perhaps a left join and IS NULL would help. That, or using SUM() instead.

Although APC's answer will be syntactically correct, if the problem you are trying to solve is indeed: "Find me all new_tags that do not have any news_tags_entries", then the query with INNER JOIN and GROUP BY and HAVING will not yield the correct result. In fact, it will always yield the empty set.
As Ignacio Vazques Abrahams pointed out, a LEFT JOIN will work. And you don't even need the GROUP BY / HAVING:
SELECT news_tags.*
FROM news_tags
LEFT JOIN news_tags_entries
ON news_tags.tag_id = news_tags_entries.tag_id
WHERE news_tags_entries.tag_id IS NULL
(Of course, you can still add GROUP BY and HAVING if you are interested to know how many entries there are, and not just want to find news_tags with zero news_tags_entries. But the LEFT JOIN from news_tags to news_tags_entries needs to be there or else you'll lose the news_tags that have no corresponding items in news_tags_items)
Another, more explicit way to solve the "get me all x for which there is no y" is a correlated NOT EXISTS solution:
SELECT news_tags.*
FROM news_tags
WHERE NOT EXISTS (
SELECT NULL
FROM news_tags_entries
WHERE news_tags_entries.tag_id = news_tags.tag_id
)
Although nice and explicit, this solution is typically shunned in MySQL because of the rather bad subquery performance

SELECT
new_tags.tag_id, new_tags.tag_name,
new_tags.tag_description,
COUNT(DISTINCT new_tags_entries.entry_id) AS entry_count
FROM (new_tags)
LEFT JOIN new_tags_entries ON new_tags_entries.tag_id = new_tags.tag_id
WHERE `new_tags`.`tag_name` LIKE '%w'
GROUP BY new_tags.tag_id ORDER BY tag_name ASC
HAVING `entry_count` < '1'

Related

Where clause applied to only one column in join

I'm having some trouble with writing a certain SQL query. I have a wallet and a balance which I do join. The query now looks like that:
SELECT
`balances`.`id` AS `id`,
FROM
`wallet`
LEFT JOIN `balances` ON
( `wallet`.`currency` = `balances`.`currency` )
WHERE
`balances`.`user_id` = '181'
Because of the where clause, the query returns just matching records. I want to get all records from wallets table and only those from balances which do match where clause... hope I explained it well enough!
Cheers!
use subquery
SELECT w.*,t.*
FROM
wallet w
LEFT JOIN ( select * from balances where user_id = 181
) t ON w.currency =t.currency
Issue is you are applying filter on left join table wallets.
use below query.
SELECT
`balances`.`id` AS `id`,
FROM
`wallet`
LEFT JOIN (select * from `balances` `user_id` = '181') ON
( `wallet`.`currency` = `balances`.`currency` );
The question is not fully clear, but you almost definitely need an extra join clause on some sort of ID. Now there is no way to match a wallet with its balance(s). Assuming that balance have eg. a wallet_id, you'll want something like:
SELECT
`balances`.`id` AS `id`,
FROM
`wallet`
LEFT JOIN `balances` ON
(`wallet`.`id` = `balance`.`wallet_id` )
WHERE
`balances`.`user_id` = '181'
Move the condition to the ON clause. Don't use subqueries!
SELECT w.*, b.id
FROM wallet w LEFT JOIN
balances b
ON w.currency = b.currency AND
b.user_id = 181;
Notes:
The subquery in the FROM can impede the optimizer.
If you are using a LEFT JOIN, you should be selecting columns from the first table.
I am guessing that user_id is a number, so I removed the quotes around the comparison value.
Table aliases make the query easier to write and to read.
Backticks make the query harder to write and harder to read.

Comparing two sum function in where clause

I want to check that an amount of likes the users received in all their personal pictures is at least twice as large as the number of likes received in the group pictures in which they are tagged.
In case the user is not tagged in any group photo but is tagged in a personal picture that has received at least one like, it will be returned.
My Question is:
How can I make a comparison between 2 sum functions
Where one result of the sum is returned in the nested query and compared with the external query.
Can I set an auxiliary variable to enter the sum value in it and compare it?
Thanks for the helpers:)
Select distinct UIP.userID
From tblUserInPersonalPic UIP
where **sum(UIP.numOfLikes) over (Partition by UIP.userID)*0.5** >
(Select distinct U.userID, sum(P.numOfLikes) over (Partition by U.userID)
From tblgroupPictures P left outer join
tblUserInGroupPic U On P.picNum=U.picNum
group by U.userID,P.numOfLikes,P.picNum)
It's kinda hard to know for sure, and of course I can't test my answer,
but I think you can do it with a couple of left joins, group by and having:
SELECT Personal.UserId
FROM tblUserInPersonalPic Personal
LEFT JOIN tblUserInGroupPic UserInGroup ON Personal.userID = UserInGroup.UesrId
LEFT JOIM tblgroupPictures GroupPictures ON UserInGroup.picNum = GroupPictures.picNum
GROUP BY Personal.userID
HAVING SUM(GroupPictures.numOfLikes) * 2 < SUM(Personal.numOfLikes)
Please note: When posting sql questions it's always best to provide sample data as DDL + DML (Create table + insert into statements) and desired results, so that who ever answers you can test the answer before posting it.
Try using two ctes..pseudo code.Also note distinct in second query will not even work,since you are returning two columns,so i changed it it below,so that you can get that column as well
;with tbl1
as
(
select a,sum(col1) as summ
from
tbl1
)
,tbl2
as
(
select userid,sum(Anothersmcol) as sum2
from tbl2
)
select tbl1.columns,tbl2.columns
from
tbl1 t1
join
tbl2 t2
on t1.sumcol>t2.sumcol
You can't use window functions in a where clause. Define it in a subquery:
select *
from (
select sum(...) over (...) as Sum1
, OtherColumn
from YourTable
) sub
where Sum1 < (...your subquery...)

SQL single-row subquery returns more than one row?

I'm trying to get ID and USER name from one query but at the same time I'm looking in my WHERE clause if ID exist in other table. I got error:
ORA-01427: single-row subquery returns more than one row
Here is how my query look:
SELECT s.ID, s.LASTFIRST
From USERS s
Left Outer Join CALENDAR c
On s.ID = c.USERID
Where c.SUPERVISOR = '103'
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
And s.ID != (SELECT USER_ID
From RESERVATIONS
Where EVENT_ID = '56')
My query inside of where clause returns two ID's: 158 and 159 so these two should not be returned in my query where I'm looking for s.ID and s.LASTFIRST. What could cause this error?
Use not in instead of !=
!= or = are for single IDs and values, not in and in are for multiple
And s.ID not in (SELECT USER_ID
From RESERVATIONS
Where EVENT_ID = '56')
Edit: not in vs not exists
Not exists is a perfectly viable option as well. In fact, it is better to not exists than not in if there are the possibility of null values in the subquery result set - In Oracle, the existence of a null will cause not in to return no results. As a general rule, I use not in for ID, not null columns, and not exists for everything else. It may be better practice to always use not exists... personal preference I suppose.
Not exists would be written like so:
SELECT s.ID, s.LASTFIRST
From USERS s
Left Outer Join CALENDAR c
On s.ID = c.USERID
Where c.SUPERVISOR = '103'
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
And not exists (SELECT USER_ID
From RESERVATIONS r
Where r.USER_ID = S.ID
And EVENT_ID = '56')
Performance
In Oracle there is no performance difference between using not in, not exists or a left join.
Source : https://explainextended.com/2009/09/17/not-in-vs-not-exists-vs-left-join-is-null-oracle/
Oracle's optimizer is able to see that NOT EXISTS, NOT IN and LEFT JOIN / IS NULL are semantically equivalent as long as the list values are declared as NOT NULL.
It uses same execution plan for all three methods, and they yield same results in same time.
This is a formatted comment that is not related to your question.
This is slow:
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
because you are filtering on a function result.
This is logically equivalent and much faster:
And c.DATEENROLLED >= to_date('4/22/2016','fmmm/fmdd/yyyy')
Edit starts here
Aaron D's answer says to use not in. Here are two faster ways to do the same thing:
left join reservations r on s.id = user_id
and r.event_id = '56'
etc
where r.user_id is null
or
where s.id in
(
select user_id
from reservations
minus
select user_id
from reservations
where event_id = 56
)

Selecting only duplicate rows, kinda

I'm really not sure on the best way to explain this, but we have a database which has a unique ID for each employee, a description, and a flag for current.
SELECT COUNT("Current_Flag"),
"Employee_Number"
FROM "Employment_History"
WHERE "Current_Flag" = 'Y'
GROUP BY "Employee_Number" ;
I'm trying to return the unique ID for every case where they have two current flags set, but I don't even know where to begin. I've tried a subselect which didn't work, I'm sure the answer is quite simple but I've just been told I only have 7 minutes to do it, so panicked and thought I'd ask here. Sorry all :/
Add a HAVING clause to your current query - like so:
select count("Current_Flag"),
"Employee_Number"
from "Employment_History"
where "Current_Flag" = 'Y'
group by "Employee_Number"
having count("Current_Flag") >= 2
(Change the condition to =2 if you only want exactly 2 matches.)
Try something like this (untested!). You might have to play around with HAVING/COUNT to get it to work
SELECT unique_id, description, flag FROM table GROUP BY unique_id HAVING COUNT(unique_id) > 1
select employee_name, count(employee_name) as cnt
group by employee_name
where cnt>1
I guess this will do the trick. Adapt it to the fields on your DB.
Self Join table return only rows where 2 "Flags" are alike exclude rows that match on UniqueID returning al instances of ID having matches on flags.
Select A.ID
from table A
INNER JOIN table B
on A.Flag1=B.Flag1 and A.Flag2=B.Flag2
Where A.ID <> B.ID
(UNTESTED STATEMENT)
SELECT Id FROM TABLE1 T1 WHERE T1.Id NOT IN(
SELECT Id FROM TABLE2 T2 WHERE (T1.Flag1 = T2.Flag1) AND (T1.Id <> T2.Id))

sql multiple count

I have 3 tables, where the first one's primary key, is the foreign key in the other 2.
I want to extract one field from the first table, and then a count from the other 2, all joined using the pk and fk. This is what I have so far:
SELECT MBDDX_STUDY.STUDY_NAME, COUNT(MBDDX_EXPERIMENT.STUDY_ID) AS NUMBER_OF_EXPERIMENTS
FROM MBDDX_STUDY
INNER JOIN MBDDX_EXPERIMENT
ON MBDDX_STUDY.ID=MBDDX_EXPERIMENT.STUDY_ID
INNER JOIN (SELECT COUNT(MBDDX_TREATMENT_GROUP.GROUP_NO) AS NUMBER_OF_GROUPS
FROM MBDDX_TREATMENT_GROUP)
ON MBDDX_TREATMENT_GROUP.STUDY_ID = MBDDX_STUDY.ID
group by MBDDX_STUDY.STUDY_NAME, MBDDX_TREATMENT_GROUP.STUDY_ID
But, i get an error saying that the MBDDX_TREATMENT_GROUP.STUDY_ID , in the penultimate line is an invalid indentifier. It is a correct table.
Any advise please.
Thanks.
You're getting the error because that column is not in your SELECT, so it can't GROUP BY a field it doesn't have.
The subquery syntax doesn't seem to make any sense to me. You've made a query that counts all rows of MBDDX_TREATMENT_GROUP, independently of the STUDY_ID, and then tries to join it into the table with a join condition that doesn't refer to anything in the subquery's results (and can't, without an alias).
Why not use a simple join? Assuming MBDDX_EXPERIMENT also has a primary key ID, you can do it with a COUNT-DISTINCT:
SELECT
MBDDX_STUDY.ID, MBDDX_STUDY.STUDY_NAME,
COUNT(DISTINCT MBDDX_EXPERIMENT.ID) AS NUMBER_OF_EXPERIMENTS
COUNT(DISTINCT MBDDX_TREATMENT_GROUP.GROUP_NO) AS NUMBER_OF_GROUPS
FROM
MBDDX_STUDY
INNER JOIN MBDDX_EXPERIMENT ON MBDDX_EXPERIMENT.STUDY_ID=MBDDX_STUDY.ID
INNER JOIN MBDDX_TREATMENT_GROUP ON MBDDX_TREATMENT_GROUP.STUDY_ID=MBDDX_STUDY.ID
GROUP BY
MBDDX_STUDY.ID, MBDDX_STUDY.STUDY_NAME
(MBDDX_STUDY.STUDY_NAME technically shouldn't be necessary to include in the GROUP BY expression according to ANSI SQL as it has a functional dependency on STUDY_ID. However it is necessary on Oracle, which can't spot the dependency.)
You don't need to group by this field (MBDDX_TREATMENT_GROUP.STUDY_ID). It should be just group by MBDDX_STUDY.STUDY_NAME
If my understanding is correct,You need a record from first table and have the count of related records in the other two tables.Here is the answer
SQL:Getting count from many tables for a user record in USER table.Whats the best approach?
It looks like you need to alias the second subquery and need to include something to join on.
It also looks like you aren't using the count you have in the subquery as well.
Try this out:
SELECT MBDDX_STUDY.STUDY_NAME
, COUNT(MBDDX_EXPERIMENT.STUDY_ID) AS NUMBER_OF_EXPERIMENTS
FROM MBDDX_STUDY
INNER JOIN MBDDX_EXPERIMENT
ON MBDDX_STUDY.ID=MBDDX_EXPERIMENT.STUDY_ID
INNER JOIN (SELECT STUDY_ID, COUNT(MBDDX_TREATMENT_GROUP.GROUP_NO) AS NUMBER_OF_GROUPS
FROM MBDDX_TREATMENT_GROUP GROUP BY MBDDX_TREATMENT_GROUP.STUDY_ID) xx
ON xx.STUDY_ID = MBDDX_STUDY.ID
GROUP BY MBDDX_STUDY.STUDY_NAME, xx.STUDY_ID
For what you really want to do, you want OUTER JOINs.
WITH number_of_experiments
AS ( SELECT study_id
, count ( * ) CNT
FROM MBDDX_EXPERIMENT
group by study_id )
, number_of_groups
as ( select study_id
, count ( * ) CNT
FROM mbddx_treatment_group
group by study_id )
select study_name
, coalesce(noex.cnt,0)
, coalesce(notr.cnt,0)
from mbddx_study
outer join number_of_experiments
as noex
using ( study_id )
outer join number_of_groups
as nogr
using ( study_id )