SQL Query based on value of a different row - php

I'm trying to query data from multiple tables but cannot seem to achieve this type of JOIN. For example, I have one table with contains products, and a wp_usermeta table with various user meta data:
Products Table
Serial # Product
---------------------
0001 A
0002 B
0003 C
wp_usermeta (simplified)
User ID Key Value
--------------------------------
1 Serials 0001
1 Company Company A
2 Serials 0001
2 Company Company B
3 Serials 0001
3 Company Company C
I need to get the Company name based on the serial numbers they own, such as:
Serial # Company
-------------------------
0001 Company A
0002 Company B
0003 Company C
I've been able to retrieve the User ID based on serial number with a simple join query, but I dont know where to go from there.
SELECT products.serial_number, wp_usermeta.user_id FROM products LEFT JOIN wp_usermeta ON products.serial_number = wp_usermeta.meta_value

I am not sure what the products table has to do with the results you need.
One method is a "simple" self-join:
SELECT p.value as serial_number, c.value as Company.user_id
FROM wp_usermeta p JOIN
wp_usermeta c
ON p.user = c.user AND
p.key = 'Serials' AND
c.key = 'Company';

You need 2 joins on wp_usermeta like this:
SELECT Products.Serial, w2.Value AS Company
FROM Products
# Detect User Id by serial number
JOIN wp_usermeta w1 ON w1.Key = 'Serials'
AND w1.Value = Products.Serial
# And select company by detected User Id
JOIN wp_usermeta w2 ON w2.Key = 'Company'
AND w2.User = w1.User

Related

In a query of three tables. How to get an array of teams with their members in SQL

I have 3 tables: Teams, Members & Records. How to make an SQL query so i can list all teams with their members.
T1: Teams table
id_team name
------------------------
1 Green team
2 Blue Team
T2: Members table
id_member name
------------------------
1 John
2 Lola
3 Nancy
4 Peter
T3: Records table
id_record id_team id_member
-----------------------------------
1 2 3
2 1 2
3 1 3
4 2 4
This is what I have for now.
$teams = lib::$db->GetAll("SELECT SQL_CALC_FOUND_ROWS
t.*,
t.name AS team_name
FROM teams AS t
LEFT JOIN records r ON t.id_team = r.id_team
LEFT JOIN members m ON r.id_members = m.id_member
");
I need to list all teams with their members. What I have above is listing all members that have a team.
Something like this:
Green team has Lola & Nancy as members.
Blue team has John & Peter as members.
Select t.name, m.name
FROM records as r
LEFT JOIN teams t ON t.id_team = r.id_team
LEFT JOIN members m ON m.id_member == r.id_member
Also keep in mind that generally you should name tables after singular nouns not plural nouns and the primary key id columns could be named better (i.e. just id) since its already assumed.
The above solution will not show teams that do not have any records since its a left join on the records table. If you wanted to see teams/members that do not have records you would need to do a full join.
SELECT T.NAME, M.NAME
FROM TEAMS T JOIN RECORDS R ON T.ID_TEAM=R.ID_TEAM
JOIN MEMBERS M ON R.ID_MEMBER=M.ID_MEMBER
Try this it will works.................
So I came up with this so far and it works. Please comment if syntax is OK or improve if possible.
$teams = lib::$db->GetAll("SELECT
t.*,
t.name AS team_name
FROM teams t
GROUP BY t.id_team");
if (!empty($teams )) {
foreach($teams as &$team) {
$teamId= (int) $team['id_team'];
$teamMember = lib::$db->GetAssoc("SELECT
r.*,
GROUP_CONCAT(m.name) AS member_name
FROM records AS r
LEFT JOIN members AS m ON r.id_member = m.id_member
WHERE id_team = $teamId
GROUP BY r.id_record");
$team['records'] = $teamMember;
}
}

MySQL Multi-Join Query

I have multiple tables I am trying to grab data from in a single query. I seem to be close to a solution but can not seem to get the data result I am expecting.
Examples of my tables are as follows (fields have been truncated):
Table c
id
name
abbreviation
Table mr (relationship table tat ties tables c and m together by ID)
id
c.id
m.id
Table m
id
Table cnt
id
c.id
Table cmp
id
cnt.id
active
What I WANT is all fields from C, all fields from M where m.id = c.id, all active (active = 1) id's from CMP that match on cnt.id.
My most recent query (after dozens of iterations) is:
SELECT c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, c.active AS active
, c.last_modified AS last_modified
, c.modified_by AS modified_by
, mr.media_id
, mr.related_object_table
, mr.related_object_id
, m.orig_name AS img_name
, m.unique_name AS img_slug
, m.file_type AS confed_file_type
, m.file_size AS file_size
, COUNT('cmp.id') AS comps
FROM confederations AS c
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = c.id
AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
INNER JOIN countries AS cnt
ON cnt.confederations_id = c.id
INNER JOIN competitions AS cmp
ON cmp.countries_id = cnt.id
AND cmp.active = 1;
I am not proficient with Joins.
Basically, the result i am expecting is: For each Confederation (table C) I want that confederations name, abbreviation, active status (active), last modified date, modified by; from the Media Relationship table (table MR) I want the image id associated with that confederation so I can use that id to grab the image name and image slug for the confederations primary image from the Media table (M).
Now I also want the total number of Competitions (table CMP) for a given Confederation. Competitions are stored with a Country ID that is tied to the primary key ID of a country in the Countries Table (table CNT). Each Country in table CNT has a Confederations ID. So to get the total number of Competitions per Confederation I am 'trying' to get all Countries within their respective Confederation by CONFEDERATIONS_ID in table CNT, then foreach confederation I want select all the competitions from table CMP with matching COUNTRIES_ID from the group of country id's for that given confederation. (At this point i think i am confusing myself with how to get what i want)
Somehow I am getting the CORRECT NUMBER of competitions, but I am getting duplicate Confederations as results. For Example I am getting something similar to this (assume I have 3 different confederations with 2, 1, and 3 competitions respectively):
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 2;
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 1;
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 3
What am i doing wrong?
Through trial and error, I actually solved this on my own. I came back to post my answer and see Degan's answer, and though it is written differently than mine I think its very close to what I ended up with:
SELECT
cnf.id AS confed_id, cnf.name AS confed_name, cnf.abbreviation AS
confed_abbr, cnf.active AS confed_active, cnf.modified_by AS
confed_mod_by, cnf.last_modified AS confed_last_mod,
COUNT(cnt.id) AS total_countries,
COUNT(cmp.id) AS total_comps,
mr.media_id, mr.related_object_table, mr.related_object_id,
mr.primary_img,
m.orig_name AS img_name, m.unique_name AS img_slug, m.file_type AS file_type
FROM confederations AS cnf
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = cnf.id AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
LEFT JOIN countries AS cnt
ON cnt.confederations_id = cnf.id AND cnt.active = 1
LEFT JOIN competitions AS cmp
ON cmp.countries_id = cnt.id AND cmp.active = 1
GROUP BY cnf.id
So farthis seems to be giving me results i can use. I am not certain if my choice of Left Join for all my joins is in fact giving me everything i need (it SEEMS to be) and whether this will omit/add records once the tables get larger. If anyone can point out a problem in my query and my choice of using Left Join as opposed to a combination of LEFT and INNER JOINs as Degan did, that would be helpful.
When aggregating you need to group by.
Perhaps this is close to what you are looking for:
SELECT c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, m.orig_name AS img_name
, SUM('cmp.id') AS comps
FROM confederations AS c
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = c.id
AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
INNER JOIN countries AS cnt
ON cnt.confederations_id = c.id
INNER JOIN competitions AS cmp
ON cmp.countries_id = cnt.id
AND cmp.active = 1
GROUP BY c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, m.orig_name AS img_name

Query to get the number of times an item appears

I have a table like this in my db:
id --- owner --- product ---- type
0 --- john --- mustang ---- car
1 --- tim --- a360 ---- plane
2 --- john --- camry ---- car
3 --- dan --- a380 ---- plane
4 --- tim --- ninja ---- bike
5 --- dan --- accord ---- car
I'm trying to get the number of each type an owner has. Something like this:
John
Car = 2
Plane = 0
Bike = 0
-------------
Tim
Car = 0
Plane = 1
Bike = 1
-------------
Dan
Car = 1
Plane = 1
Bike = 0
-------------
I have been unable to solve this.
Another issue is that my database is able to accept new types. For example, someone can add a bicycle as a type.
Is there any way to do this?
The way I approached this was a little tricky.
I started by creating one result set that used a cartesian product to get one row for each person and type combination.
SELECT m.owner, t.type
FROM myTypes t, myTable m
GROUP BY m.owner, t.type
Then, I made another result set that grabbed the owner, type, and the number of each type for that owner. However, this only returned rows for existing owner-type combinations. It would not return any values for say 'John' and 'Plane' because he does not have a plane product.
SELECT m.owner, t.type, COUNT(*) as numOfType
FROM myTypes t
JOIN myTable m ON t.type = m.type
GROUP BY t.type, m.owner;
Finally, I joined those two tables together using an outer join so I received every row from the owner-type combinations table. Of course, some rows returned null for the count, so I had to use IFNULL to replace them with 0. This matches the result set in your question.
SELECT w1.owner, w1.type, IFNULL(w2.numOfType, 0) AS numOfType
FROM (SELECT m.owner, t.type
FROM myTypes t, myTable m
GROUP BY m.owner, t.type) w1
LEFT JOIN (SELECT m.owner, t.type, COUNT(*) as numOfType
FROM myTypes t
JOIN myTable m ON t.type = m.type
GROUP BY t.type, m.owner) w2
ON w1.owner = w2.owner AND w1.type = w2.type;
Here is the SQL Fiddle.
To solve this problem, first you have to generate the output rows and then get the counts:
select o.owner, p.product, count(t.owner)
from (select distinct owner from table) o cross join
(select distinct product from table) p left join
table t
on t.owner = o.owner and t.product = p.product
group by o.owner, p.product;
If you have reference tables for owners and products, then you can use these instead of the select distinct subqueries.
EDIT:
Grouping by type is basically the same idea:
select o.owner, ty.type, count(t.owner)
from (select distinct owner from table) o cross join
(select distinct type from table) ty left join
table t
on t.owner = o.owner and t.type = ty.type
group by o.owner, ty.type;

Getting count from inner join table

I have tables as described below:
subscription_plans (Table for storing all plans)
id plan days_limit added_on status rate
------------------------------------------------
1 PlanA 15 1398249706 1 150.00
2 PlanB 15 1398249706 1 150.00
subscribed_videos (Table for storing details of video in each plans)
id plan_id videoid
----------------------
1 1 1
2 2 2
subscription_groups (Table for storing groups where a plan can be part of another plan. ie, Plan A be a plan with 2 other individual plans, Plan B and C )
id plan_id assosiated_plan_id added_on
----------------------------------------------
1 1 2 1398249706
usersubscription (Table for storing user subscribed plans)
id user_id plan_id subscribed_on
---------------------------------------
1 1 1 1398771106
Now, my problem is that how can I get the count of videos for each plans. If Plan A contains both Plan B and C (subscription_groups table), then the count should return the total video count for each individual plans in that particular plan. Now I have done with a query which will return plan details along with count of videos for a plan but I am not able to join it with subscription_groups. How can I accomplish this in a single query.
$data['planquery']=$this->db->query("select
us.plan_id,us.subscribed_on,sp.plan,sp.days_limit,sp.rate,count(sv.videoid) from
usersubscription as us INNER JOIN
subscription_plans as sp ON us.plan_id=sp.id INNER JOIN subscribed_videos as sv ON sp.id=sv.plan_id where sp.status=1 and us.user_id=1");
Expected Result:
plan_id subscribed_on plan days_limit rate count
-------------------------------------------------------
1 1398771106 PlanA 15 150.00 2
Can anyone help me to find a solution for this?
Thanks in advance.
You can do so
SELECT
us.plan_id,
us.subscribed_on,
sp.plan,
sp.days_limit,
sp.rate,
COUNT(sv.videoid)
FROM
usersubscription AS us
RIGHT JOIN subscription_plans AS sp
ON us.plan_id = sp.id
INNER JOIN subscribed_videos AS sv
ON sp.id = sv.plan_id
INNER JOIN subscription_groups g
ON(g.plan_id =sv .plan_id OR sv.plan_id= g.assosiated_plan_id)
WHERE sp.status = 1
AND (us.user_id = 1 OR us.user_id IS NULL )
Demo
Since user has only plan associated but the associated plan can also has another plan linked so the last condition will check the user id but for is null to for the second linked plan user id will be null due to right join on subscription_plans
Edit
SELECT
u.plan_id,
u.subscribed_on,
p.plan,
p.days_limit,
p.rate
,COUNT(DISTINCT v.`videoid`)
FROM `usersubscription` u
JOIN `subscription_groups` g
ON (u.`plan_id` = g.`plan_id`)
RIGHT JOIN `subscription_plans` p
ON(u.`plan_id` = p.`id` OR g.`assosiated_plan_id` = p.`id`)
INNER JOIN `subscribed_videos` v ON(v.`plan_id`=g.`assosiated_plan_id` OR u.`plan_id`= v.`plan_id`)
WHERE u.`id`=1 AND p.`status` = 1
Demo 1 Demo2
For video ids you can use group_concat
SELECT
u.plan_id,
u.subscribed_on,
p.plan,
p.days_limit,
p.rate
,COUNT(DISTINCT v.`videoid`) `video_count` ,
GROUP_CONCAT(DISTINCT v.`videoid`) `video_ids`
FROM `usersubscription` u
JOIN `subscription_groups` g
ON (u.`plan_id` = g.`plan_id`)
RIGHT JOIN `subscription_plans` p
ON(u.`plan_id` = p.`id` OR g.`assosiated_plan_id` = p.`id`)
INNER JOIN `subscribed_videos` v ON(v.`plan_id`=g.`assosiated_plan_id` OR u.`plan_id`= v.`plan_id`)
WHERE u.`id`=1 AND p.`status` = 1
Demo 1a Demo 2a

MySQL insert data from other table

This question is similar to my previous question except this is INSERT instead of update
I have two tables: contacts and companies.
contacts has : id, group_id, company_id, email, company
companies has: id, group_id, name, email
so using this query
UPDATE contacts c
INNER JOIN companies co ON c.company_id = co.id
SET c.group_id = co.group_id,
c.company = companies.name
I can move update data from company to contacts where there contact.company_id = company.id.
But how can I do INSERT instead for all the company where does not have contact yet? or to make things simpler, how can I move all companies table data into contacts table data. e.g:
Companies
id group_id name email
1 1 abc a#a.com
2 1 def d#d.com
3 1 ghi g#g.com
Contacts
id group_id company_id email company phone
1 1 1 a#a.com abc
2 1 2 d#d.com def
3 1 3 g#g.com ghi
So I would like the entry like that, and for the one that is no value will be default to NULL or None
I think that you want:
INSERT INTO Contacts (id,group_id,company_id,email,name)
SELECT co.id,co.group_id,co.id,co.email,co.name
FROM company co
LEFT JOIN contacts c ON co.id = c.company_id
WHERE c.company_id IS NULL
This will insert all the information from contacts in company that wasn't already there. the column phone will be left null, since there is no information in contacts for that column.
I believe you would need to do an outer join between the 2 tables in your select statement then look for a field in your destination table to be null and only select those rows.
insert into t1 (f1,f2,f3) select s1,s2,s3 from sourcetable as st
left outer join t1 on
t1.f1=st.s1,
t1.f2=st.s2,
t1.f3=st.s3
where t1.id is null;
This way you only get the rows from st where there is no id in t1.

Categories