JOIN - showing missing rows but attaching them to specific column - mysql

Two tables
MEDIA_APPROVALS
[media_id] [firm_id] [approval_status]
FIRM_LIST
[firm_id] [firm_code]
End goal is to show what media is NOT approved by what firm.
By default, all media listed in the MEDIA_APPROVALS table is NOT approved by the firms listed in the FIRM_LIST table. So in order for media to be approved, the row must contain a firm_id with approval_status=1.
If a media piece is in the MEDIA_APPROVALS table & the row contains a firm_id and approval_status=0, that's easy - it's NOT approved.
Where it gets tricky to me is: if there is a MISSING ROW in the MEDIA_APPROVALS table for a media_id/firm_id connection, then that media_id is NOT approved for that firm.
Ultimately I want to arrive at this:
MEDIA_APPROVALS
100 1 1
100 2 1
101 1 0
101 2 0
101 3 1
102 1 1
FIRM_LIST
1 AA
2 BB
3 CC
QUERY OUTPUT
100 CC 0
101 AA 0
101 BB 0
102 BB 0
102 CC 0
I am a PHP/web programmer and NOT a db admin. The help is HUGELY appreciated!

So you need every possible combination of Media and Firm? Ooh, that's a CROSS JOIN. We don't get to do those very often:
WITH MediaFirms As (
SELECT m_id.media_id, f_id.firm_id
FROM (SELECT DISTINCT media_id FROM MEDIA_APPROVALS) m_id
CROSS JOIN (SELECT DISTINCT firm_ID FROM FIRM_LIST) f_id
)
SELECT mf.media_id, mf.firm_id, coalesce(ma.approval_status, 0) "approval_status"
FROM MediaFirms mf
LEFT JOIN MEDIA_APPROVALS ma ON ma.media_id = mf.media_id AND ma.firm_id = mf.firm_id
WHERE coalesce(ma.approval_status,0) <> 1;

Related

Trying to get latest status for related shipment but the results I receive are incorrect

I am currently working on a project while trying to learn MySQL and I would like to join three tables and get the latest status for each related shipment. Here are the tables I'm working with (with example data):
shipments
id
consignee
tracking_number
shipper
weight
import_no
1
JOHN BROWN
TBA99900000121
AMAZON
1
101
2
HELEN SMITH
TBA99900000190
AMAZON
1
102
3
JACK BLACK
TBA99900000123
AMAZON
1
103
4
JOE BROWM
TBA99900000812
AMAZON
1
104
5
JULIA KERR
TBA99900000904
AMAZON
1
105
statuses
id
name
slug
1
At Warehouse
at_warehouse
2
Ready For Pickup
ready_for_pickup
3
Delivered
delivered
shipment_status (pivot table)
id
shipment_id
status_id
1
1
1
2
2
1
3
3
1
4
4
1
5
5
1
6
1
2
7
2
2
8
3
2
9
4
2
10
5
2
all tables do have created_at and updated_at timestamp columns
Example of the results I'm trying to achieve
slug
shipment_id
status_id
ready_for_pickup
1
2
ready_for_pickup
2
2
ready_for_pickup
3
2
ready_for_pickup
4
2
ready_for_pickup
5
2
Here's the query I wrote to try to achieve what I'm looking for based on examples and research I did during the past couple of days. I find that sometimes there is sometimes a mismatch with the latest status that relates to the shipment
SELECT
statuses.slug AS slug,
MAX(shipments.id) AS shipment_id,
statuses.id AS status_id,
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
GROUP BY
`shipment_id`
Because we need to reference other fields from the same record that evaluates from the MAX aggregation, you need to do it in two steps, there are other ways, but I find this syntax simpler:
SELECT
shipments.id AS id,
statuses.slug AS slug,
statuses.id AS status_id,
shipment_status.shipment_id as shipment_id
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
WHERE
shipment_status.id = (
SELECT MAX(shipment_status.id)
FROM `shipment_status`
WHERE shipment_status.shipment_id = shipments.id
)
try it out!
This query makes the assumption that the id field is an identity column, so the MAX(shipment_status.id) represents only the most recent status for the given shipment_id
You can use window functions:
SELECT s.id, st.slug, st.id
FROM shipments s JOIN
(SELECT ss.*,
ROW_NUMBER() OVER (PARTITION BY shipment_id ORDER BY ss.id DESC) as seqnum
FROM shipment_status ss
) ss
ON ss.shipment_id = s.id JOIN
statuses st
ON ss.status_id` = st.id
WHERE ss.seqnum = 1;
Also note the use of table aliases so the query is easier to write and to read.

Mysql join not showing all records

I am using Php with join, I have two tables ("services" and "service_detail"), I want to get all services
but want to know which service selected or notselected by vendor
Here is my services table strcture
id service_name image
1 Abc abc.jpg
2 xyz xyz.jpg
3 OPS ops.jpg
4 tys tys.jpg
5 byp byp.jpg
Here is my services_detail table strcutre
id vendor_id service_id price
1 101 1 50
2 101 2 70
3 101 3 80
4 101 4 30
5 102 1 70
6 102 2 40
...
I tried with following query but showing only selected services, but i want to get all services with parameter ( selected or notselected)
Where i am wrong ? Here is my query
SELECT sd.vendor_id, sd.service_id, sd.price, s.service_name, s.image
FROM services_detail sd
LEFT JOIN services s
ON sd.service_id = s.id
WHERE sd.vendor_id = '101'
Move your where clause to be AND in ON clause:
AND sd.vendor_id = '101'
And interchange tables in join to get all servcies
SELECT sd.vendor_id, sd.service_id, sd.price, s.service_name, s.image,
IF (sd.vendor_id is not null, 'Opted', 'Not opted') as status
FROM services s
LEFT JOIN services_detail sd
ON sd.service_id = s.id AND sd.vendor_id = '101';
In simple words, when there is a where clause including filters on table of Left Join then it will act like INNER JOIN not LEFT JOIN.

Mysql select statement contains where clause so unsuitable for insert into

I'm very inexperienced. I've prepared a select statement which gives the information I need to populate a matches table. However it is not suitable because it contains a where clause. Is there a different way to use it, or how can I change it so that it is suitable for INSERT INTO.
The tables are as follows:-
match_order
match_order_id||match_descrip||first_player||second_player
1 1v2 1 2
2 1v3 1 3
3 2v3 2 3
4 1v4 1 4
5 2v4 2 4
6 3v4 3 4
entries
entry_id||round_id||league_id||box_id||box_position
1 1 1 1 1
2 1 1 1 2
3 1 1 1 3
4 1 2 1 4
5 1 2 1 2
6 1 2 1 1
7 1 2 1 1
matches
match_id||round_id||league_id||box_id||match_order_id||player1||player2
I need to insert new rows every month for a new round of matches. League size, box size & positions change each month.
This is the statement which gives the correct rows.
SELECT e.round_id, e.league_id, e.box_id, mo.match_order_id, e.entry_id as player1, e1.entry_id as player2
FROM match_order mo
LEFT JOIN entries e ON mo.first_player = e.box_position
LEFT JOIN entries e1 ON mo.second_player = e1.box_position
WHERE e.round_id = e1.round_id AND e.league_id = e1.league_id AND e.box_id = e1.box_id
ORDER BY round_id, league_id, box_id, match_order_id
Any help & advise would be greatly appreciated.
Thank you
Assuming match_id is an auto-increment column, you have the data for the other columns. You can just add the INSERT statement before your SELECT.
INSERT INTO matches(round_id, leage_id, box_id, match_order_id, player1, player2)
SELECT e.round_id, e.league_id, e.box_id, mo.match_order_id, e.entry_id as player1, e1.entry_id as player2
FROM match_order mo
LEFT JOIN entries e ON mo.first_player = e.box_position
LEFT JOIN entries e1 ON mo.second_player = e1.box_position
WHERE e.round_id = e1.round_id AND e.league_id = e1.league_id AND e.box_id = e1.box_id

MySQL - Multiple Conditions and Their Results with GROUP BY

table name : users u
id username src
1 mark 101
2 stanley 102
3 john 103
4 stewe 104
table name : call_history c
id src dst duration
1 101 555-1217 20
2 555-1315 102 30
3 555-2245 102 40
4 102 555-6523 30
5 102 555-4213 20
6 555-1689 102 15
7 103 555-1775 35
There are two tables and these columns.
Conditions are;
SUM(duration) AS OutboundSUM (Condition: u.src=c.src )
COUNT(duration) AS OutboundCNT (Condition: u.src=c.src )
SUM(duration) AS InboundSUM (Condition: u.src=c.dst )
COUNT(duration) AS InboundCNT (Condition: u.src=c.dst )
What I need to see with Group By per username;
username OutboundSUM OutboundCNT InboundSUM InboundCNT
mark 20 1 0 0
stanley 50 2 85 3
john 35 1 0 0
stewe 0 0 0 0
I tried UNION ALL, sub query after select, INNER JOIN but It didn't work.
Union gives me 2 line for each username, join makes me crazy, sub queries takes longtime and wrong results.
All help is appreciated.
Problem, solved with Kicstart's solution. Thank you very much for each help.
Can you provide the primary keys of the table? It would help us give better answers.
Also, you MUST include both conditions u.src=c.src AND u.id=c.id for join.
If the IDs of the two tables do not relate to each other, I suggest you use different attribute names.
Couple of sub queries?
SELECT a.src,
oBSum AS OutboundSUM,
oBCnt AS OutboundCNT,
iBSum AS InboundSUM,
iBCnt AS InboundCNT
FROM users u
LEFT OUTER JOIN (SELECT src, SUM(duration) AS oBSum, COUNT(*) AS oBCnt FROM call_history GROUP BY src) ob
ON u.src = ob.src
LEFT OUTER JOIN (SELECT dst, SUM(duration) AS iBSum, COUNT(*) AS iBCnt FROM call_history GROUP BY dst) ib
ON u.src = ib.dst

mysql extra count field for each row

I have a query here, anyone can help me to count the total duplicated fields?
SELECT *
FROM item
INNER JOIN itemgroup on item.itemgroupid = itemgroup.itemgroupid
INNER JOIN status on status.statusid = item.status
INNER JOIN owner on owner.ownerid = item.owner
INNER JOIN
(
SELECT code //, (SELECT count(*) FROM item WHERE ....) as 'total_duplicateds'
FROM item
GROUP BY code
HAVING count(code) > 1
) dup ON item.code = dup.code
Total items: 500
Total items with duplicated codes: 149
Now I get a total of 149 fields returned, how can I add this as a new field to each row?
After the slash is how I learnt to do it but this is a little higher level for me..
Can someone help me out?
To be even more specific
What I'd like to get returned is like:
itemid | code| itemname | itemgroup | owner | total_duplicateds
1 1000 X 1 1 3
2 1000 X 2 2 3
3 1001 A 1 1 3
4 1000 B 3 1 3
5 1002 U 2 1 3
Add COUNT aggregation and GROUP BY all columns that are interesting you.