I want to join to a table, but include All records if the table being joined to is a certain ID - sql

I want to join to a table, but include All records if the table being joined to is a certain ID.
I have a list of records with a type_id:
RECORD
id | type_id
---|---
1 | 1
2 | 1
3 | 2
TYPE
id | type_desc
---|---
1 | type1
2 | type2
3 | all
USER
id | type_id
---|---
1 | 1
2 | 3
3 | 2
Record to type is one to one, user to type is one to one, and the "Type" on a record has to be 1 or 2. User can be 1, 2 or 3. The way this would go with a normal join is
select * from record r
inner join user u on u.type_id = r.type_id
where u.user_id=:userId
But now I need to factor in that "All" type, and basically just ignore the join/return all results if the user's type is 3.
So if the user being queried is ID 1, only records 1 and 2 (type 1) would be returned. If userId is 3, only record 3 is returned. But if user ID is 2, corrresponding to the "All" type, then 1,2,3 should be returned.

I hope this helps you:
With dtAll as (
select * from user u
where u.type_id = 3
limit 1
)
select * from record r
inner join user u on u.type_id = r.type_id or exists( select * from dtAll )

Related

Count rows from left table without corresponding value in the right table

I want to count rows from left table (of a 1-to-many relation between two tables) that do not have PK-FK representative in right table
Left table
id | value
-----------
1 | a
2 | b
3 | c
Right table
id | id-left | value
--------------------
.. | 1 | ....
the expected result is 2 as rows with id 1 and 3 in left table have no counterpart in right table.
You can use a not exists anti-semi-join:
select count(*)
from l
where not exists (
select * from r where r.id_left = l.id
);

Sql join two tables with having clause

I have two tables
First_id | Text Second_id | First_id | Date | Email
I need to get all records from first table having count from second table with date null and email null.
I have sql:
Select * from first f join second s on f.id = s.first_id where date is null and email is null group by first_id having(count(s.id) < 10 or count(s.id) = 0)
It works well, but where I have all data and email filled on second table for id from first table I got no result.
Sample data:
First table
1 | one
2 | two
Second table
1 | 1 | NULL | NULL
1 | 1 | 2015-01-01 | NULL
1 | 2 | 2015-01-01 | NULL
1 | 2 | 2015-01-01 | NULL
I expect on output:
1 | one | 1
2 | two | 0
last column is count of entries from second with date and email NULL. My query returns
1 | one | 1
No second row
Do a left join, also it's good to specify which columns you want to show, otherwise you will get duplicates.
Select * from first f left join second s on f.id = s.first_id where date is null and email is null group by first_id having(count(s.id) < 10 or count(s.id) = 0)
SELECT t1.First_id, t2.Second_id
FROM t1
LEFT JOIN t2
ON t1.First_id = t2.First_id
INNER JOIN (
SELECT Second_id
FROM t2
GROUP BY Second_id
HAVING (COUNT(*) < 10 OR COUNT(*) = 0)
) _cc
ON t.Second_id = _cc.Second_id
WHERE t2.date IS NULL AND t2.email IS NULL;
A solution is to check the HAVING restrictions in a subquery that returns the ids you need for the rest joins.
When you use the GROUP BY statement it is good to select only the GROUP BY column or an aggregate function otherwise you might have unpredictable results.
https://dev.mysql.com/doc/refman/5.1/en/group-by-handling.html
How to include other grouped columns

SQL advanced query counting the max value of a group

I want to create a query that will count the number of times the following condition is met.
I have a table that consists of multiple records with a matching foreign key. I want to check only for each of the foreign key groups if the highest value of another column of that key occurs more than once. If it does that will up the count.
Data
--------------------------
ID | Foreign Key | Value
--------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 2
4 | 2 | 0
5 | 2 | 2
6 | 2 | 1
7 | 3 | 0
8 | 3 | 1
9 | 3 | 1
The query I want should return the number 2. This is because the maximum value in group 1(Foreign Key) occurs twice, the value is 2. In group 2 the maximum value is 2 but only occurs once so this will not up the count. Then in group 3 the maximum value is 1 which occurs twice which will up the count. The count therefore ends up as two.
All credit goes to the comment from #Bob, but here is the sql that solved this problem.
SELECT Count(1)
FROM (SELECT DISTINCT foreign_key
FROM (SELECT foreign_key,
Count(1)
FROM data
WHERE ( foreign_key, value ) IN (SELECT foreign_key,
Max(value)
FROM data
GROUP BY foreign_key)
GROUP BY foreign_key
HAVING Count(1) > 1) AS data) AS data;
This is one approach:
select max(num_at_max)
from (select t.*, count(val) over(partition by fk) as num_at_max
from tbl t
join (select max(max_val_by_grp) as max_val_all_grps
from (select fk, max(val) as max_val_by_grp
from tbl
group by fk) x) x
on t.val = x.max_val_all_grps) x

Select records not in another table with additional criteria

I am working on an ACCESS DB.
I have 1 table (tblData) with 1 column ( DataId) and 3 entries:
tblData (A)
+--------+
| DataId |
+--------+
| 1 |
| 2 |
| 3 |
+--------+
Another table (tblSelections) contains 3 columns (id, dataid, userid) and has 3 entries:
tblSelections (B)
+----+--------+---------+
| id | dataid | userid |
+----+--------+---------+
| 1 | 1 | 5 |
| 2 | 2 | 5 |
| 3 | 3 | 2 |
+----+--------+---------+
How can I select the records from table A (tblData) which are not in tbl B (tblSelections) for a certain 'userid'?
For 'userid' 5 the query must return 'DataId' 3 from table A as dataid 1 & 2 are already present in table B for userid 5.
For 'userid' 2 the query must return 'DataId' 1 & 2 from table A as dataid 3 is already present in table B for userid 2.
For 'userid' 1 the query must return 'DataId' 1, 2 & 3 from table A as no records are present in table B for userid 1
Use EXISTS or IN for queries like yours:
SELECT *
FROM tblData
WHERE DataId NOT IN
(
SELECT dataid
FROM tblSelections
WHERE userid = 5
);
SELECT *
FROM tblData
WHERE NOT EXISTS
(
SELECT *
FROM tblSelections
WHERE tblSelections.dataid = tblData.DataId AND tblSelections.userid = 5
);
You can use an outer join to select all records, then put a condition in the where clause that a non-nullable column in b is null. This will give you all records in a that do not have a matching row in b according to the join conditions.
This query assumes that you have a parameter or variable named #userid that represents the user ID to search against.
select
a.*
from tblData a
left join tblSelections b on b.dataid = a.dataid and b.userid = #userid
where b.id is null

Count distinct rows via a pair of known values

I wasn't even sure how to phrase this question. I'll give example content and wanted output, I'm looking for a query to do this.
Let's say I have table called "flagged" with this content:
content_id | user_id
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
2 | 4
3 | 2
3 | 3
4 | 1
4 | 2
5 | 1
6 | 1
6 | 4
And I have a a-symmetrical relationship between content_ids:
master_content_id | slave_content_id
1 | 2
3 | 4
5 | 6
For each "master" content_id (1, 3 and 5), I want to count how many distinct users have flagged either the master or the slave content, but count someone who flagged both as a single flag - which means that in the above example, content_id=1 was counted by user_id=1 (as content_id=1 and content_id=2), by user_id=2 (as content_id=1), by user_id=3 (as content_id=1 and content_id=2), and by user_id=4 (as content_id=2!)
An example of the output of the query I want to make is:
content_id | user_count
1 | 4 # users 1, 2, 3, 4
3 | 3 # users 1, 2, 3
5 | 2 # users 1, 4
I can't assume that the related content_ids are always a consecutive odd/even (i.e. 66 can be the master of the slave 58)
I am using MySQL and don't mind using its extensions to SQL (but rather the query be ANSI, or at least portable to the most databases)
The query below worked for me.
I'm using a sub-query with a UNION ALL to treat your mapped contents equal to the direct contents.
SELECT master_content_id AS content_id,
COUNT(DISTINCT user_id) AS user_count
FROM (
SELECT master_content_id, slave_content_id
FROM relationship
UNION ALL
SELECT master_content_id, master_content_id
FROM relationship
) r
JOIN flagged f ON ( f.content_id = r.slave_content_id )
GROUP BY master_content_id
Result:
content_id user_count
1 4
3 3
5 2
I think something like this will work for you (although GROUP_CONCAT is MySQL specific, similar concatenation can be achieved in other RDBMS)
SELECT COALESCE(Master_Content_ID, Content_ID) AS Content_ID,
COUNT(DISTINCT User_ID) AS Users,
CONCAT('#Users ', GROUP_CONCAT(DISTINCT User_ID ORDER BY User_ID)) AS UserList
FROM Flagged
LEFT JOIN MasterContent
ON Content_ID = Slave_Content_ID
GROUP BY COALESCE(Master_Content_ID, Content_ID)
Sample SQL Fiddle here: http://www.sqlfiddle.com/#!2/d09be/2
Output:
CONTENT_ID USERS USERLIST
1 4 #Users 1,2,3,4
3 3 #Users 1,2,3
5 2 #Users 1,4
From the samples given, does this do the job (I don't have MySQL available to test)?
SELECT
ms.master_content_id,
(SELECT COUNT(DISTINCT f.user_id) FROM flagged f WHERE
f.content_id = ms.slave_content_id OR
f.content_id = ms.master_content_id)
FROM
master_slave ms
It would be better not to have the DISTINCT, but I can't see a way around it.
SELECT master_content_id AS content_id
, COUNT(*) AS user_count
, GROUP_CONCAT(user_id) AS flagging_users
FROM
( SELECT r.master_content_id
, f.user_id
FROM relationship AS r
JOIN flagged AS f
ON f.content_id = r.master_content_id
UNION
SELECT r.master_content_id
, f.user_id
FROM relationship AS r
JOIN flagged AS f
ON f.content_id = r.slave_content_id
) AS un
GROUP BY master_content_id