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;
Related
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;
}
}
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
I have 3 table: user , company and deal.
One user may own several companies. Deal is made between the 2 companies. I need a list of deals, which involved my company.
Deals must contain the following fields: partner_company_id,my_company_id,partner_photo,partner_name,deal_about.
Language code: PHP.
Database: Mysql.
1.List of my company I can get by user ID.
user_id = 22;
companyList = query('SELECT company_id FROM company WHERE user_id = ?', user_id);
2. Then i get deal list where my_company_id is company_first_id
list1 = query('SELECT u.name AS partner_name, u.photo AS partner_photo, d.first_company_id AS
my_company_id , d.second_company_id AS partner_company_id,d.about AS deal_about FROM deal AS d
INNER JOIN company AS c ON c.company_id = d.second_company_id
INNER JOIN user AS u ON u.user_ud = c.user_id
WHERE d.company_first_id IN (?)', companyList);
3. Then i get deal list where my_company_id is company_second_id
list2 = query('SELECT u.name AS partner_name, u.photo AS partner_photo, d.first_company_id AS
partner_company_id , d.second_company_id AS my_company_id,d.about AS deal_about FROM deal AS d
INNER JOIN company AS c ON c.company_id = d.first_company_id
INNER JOIN user AS u ON u.user_ud = c.user_id
WHERE d.company_second_id IN (?)', companyList);
4. then i marge to array and set limit list
list = array_marge(list1,list2);
result = array_slice (list ,0 , 10);
HELP please optimize this queries.
THANKS.
DATABASE SCHEME
user | company | deal |
--------------------------------------------------
user_d | company_id | deal_id
photo | user_id |first_company_id
name | about |second_company_id
| |description
Are your queries so slow? They don't look slow (provided you have indexes on all IDs of course).
However, you can save one database access by combining the two deal queries. Either you simply select query1 UNION ALL query1 or you do it in one pass:
select
u.name AS partner_name,
u.photo AS partner_photo,
d.my_company_id,
d.partner_company_id,
d.about AS deal_about
from
(
select
about,
case when company_first_id in (?) then
company_first_id
else
company_second_id
end as my_company_id,
case when company_first_id in (?) then
company_second_id
else
company_first_id
end as partner_company_id
from deal
where company_first_id in (?) OR d.company_second_id in (?)
) as d
inner join company as c on c.company_id = d.partner_company_id
inner join user as u on u.user_ud = c.user_id
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
Hello I have two tables:
table1 - eod_stock
company_code | open | high | low | ltp | close | ycp | total_volume | total_value | datetime
table 2 - company
ID | code | name
here company code = code so to get all name and other info i used this code:
but first one gives me error and 2nd one returns only one row, but i need all 200 companies with their associated info.
select
company.code,
company.name,
eod_stock.open,
eod_stock.high,
max(eod_stock.datetime)
from
company
right join company on company.code= eod_stock.company_code;
and
select
eod_stock.company_code,
max(eod_stock.close),
eod_stock.total_volume,
eod_stock.total_trade,
eod_stock.high,
eod_stock.low,
eod_stock.ltp,
max(eod_stock.datetime),
company.name
from
eod_stock
inner join company on (company.code = eod_stock.company_code);
but first one gives me error and 2nd one returns only one row, but i need all 200 companies with their associated info.
The trick here is to start with a list of the max datetime for each company_code, which you can do with this basic query:
SELECT company_code, MAX(datetime) AS maxdatetime
FROM eod_stock
GROUP BY company_code
Join this to a query that gets company code, company name, and end-of-day values, and you should be all set:
SELECT
company.code,
company.name,
eod_stock.open,
eod_stock.high
FROM eod_stock
INNER JOIN company ON company.code = eod_stock.company_code
INNER JOIN (
SELECT company_code, MAX(datetime) AS maxdatetime
FROM eod_stock
GROUP BY company_code) maxdt
ON maxdt.company_code = eod_stock.company_code AND
maxdt.maxdatetime = eod_stock.datetime
Your first error i guess that your have to write :
Table2 right join Table1
instead of company right join company
the 2nd one to get all company your full join !!
select
eod_stock.company_code,
max(eod_stock.close),
eod_stock.total_volume,
eod_stock.total_trade,
eod_stock.high,
eod_stock.low,
eod_stock.ltp,
max(eod_stock.datetime),
company.name
from
eod_stock
inner join company on (company.code = eod_stock.company_code) group by eod_stock.company_code;
dagfr was correct all i needed to add group by in the query.