I habe a main table that i select from and a table with subelements that i select from in a join. Example:
person skill person_to_skill
id | name id | skill id | p_id | s_id
------------ ------------ ----------------
1 | jim 1 | sewing 1 | 1 | 2
2 | peter 2 | cooking 2 | 2 | 1
3 | susan 3 | singing 3 | 2 | 3
4 | kevin 4 | 3 | 1
5 | 3 | 2
6 | 4 | 3
So now we see, sim has only one skill, peter has two and so forth.
Now if i select from person, koin skill and then also join person_to_skill, but i only want two persons. How do i manage to do so without grouping and thereby not getting all the skills?
Shortly: I want to select two persons from "person" with all their skills.
I tried just using LIMIT but that limits the result rows, not the persons.
If i use GROUP BY i only get one skill per person.
Is this possible without a subselect?
Any ideas anyone?
My Approach so far, changed to work with the example, looks like this:
SELECT p.id,p.name,s.skill
FROM person AS p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
ORDER BY p.name
LIMIT 0,2
Limit number of persons at very beginning in subquery then join to them other tables as you've already done:
SELECT p.id,p.name,s.skill
FROM (select * from person ORDER BY name LIMIT 0,2) AS p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
Added after comment:
If you really can't use subqueries you can do it using two queries. Firstly select users ids:
select id from person ORDER BY name LIMIT 0,2
and then use those ids in next query:
SELECT p.id,p.name,s.skill
FROM person p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
WHERE p.id IN (ids from previous query)
You can do something like
SELECT p.id, p.name, group_concat(s.skill separator ',')
and then group by person and limit the number of rows.
Related
I have two tables
tbl_groups:
id | name
----------------
1 | BSCS
2 | BSIT
3 | BBA
tbl_students:
id | name | group_id
-------------------------------
1 | Student Name | 1
2 | Student 2 | 1
3 | Student 3 | 2
I want to show groups details: group name and number of students in a particular group,
I am using this query but it shows groups that has students. it does not show group with 0 students.
select tb2.id, tb2.name, count(*) from tbl_students tb1 JOIN tbl_groups tb2 ON tb1.group_id = tb2.id
How do I show all groups, please give me some idea
EDIT:
if I use above query I get following result:
id | name | count(*)
-------------------------------
1 | Student Name | 2
2 | BSIT | 1
(it doest show 3rd group because there are 0 students, I want to show this groups also).
Just use a left join:
select tb2.id, tb2.name, count(tb1.id) as no_std
from tbl_groups tb2
LEFT JOIN tbl_students tb1 ON tb2.id = tb1.group_id
group by tb2.id, tb2.name
See it working live here: http://sqlfiddle.com/#!9/2282a3/5
I would just use a correlated subquery to get the count of students in each group, like so:
select
g.*,
(select count(*) from tbl_students s where s.group_id = g.id) no_students
from tbl_groups g
This does not filter out groups that have no students (it will give a count of 0 instead). And with an index on tbl_students(group_id), this should be as efficient as it gets (this index is already there if you set up a foreign key constraint on that column - as you should have).
I want to use an join to list the car colors count, car type, and users name.
I have 3 table
Table 1 Useres
id|username|fullname
1 | test0 | xy xy
2 | test1 | yx yx
Table 2 Car Type
id|car_type|user_id
1 | Ford | 1
2 | BMW | 2
3 | Ford | 1
4 | Skoda | 1
5 | BMW | 2
Table 3 Car Color
id| Color |user_id|car_id
1 | Red | 1 |1
2 | Blue | 2 |2
3 | Red | 2 |5
4 | Red | 1 |3
5 | Red | 1 |4
6 | Green | 1 |4
One car has 2 color
The result should be:
countType | CountColor | UserName
3 | 4 | test0
2 | 2 | test1
I tryed this:
SELECT
test as BlogsPost,
test2 as CommenstPost,
u.name
FROM users u
LEFT JOIN (
select COUNT(blogs.user_id) as test FROM blogs GROUP by blogs.user_id) blogs
on blogs.user_id=u.id
LEFT JOIN (
select COUNT(comments.user_id) as test2 FROM comments GROUP by comments.user_id) comments
on comments.user_id=u.id
GROUP by users.id
If I understand your question correctly with reference to your actual code section what you want is a list of users with how many blogs they have and how many comments they have. Now if you were wanting to count one matching table you could just do this:
SELECT
U.NAME
,COUNT(1) AS BLOG_COUNT
FROM USERS U
LEFT JOIN BLOGS B
ON B.USER_ID = U.ID
GROUP BY U.NAME
But since you are wanting to count two tables you have to do it slightly differently. There's a few ways of doing it but the way I like is like this:
SELECT
U.NAME
,B.BB_COUNT AS BLOG_COUNT
,C.CC_COUNT AS COMMENT_COUNT
FROM USERS U
LEFT JOIN
(
SELECT
BB.USER_ID
,COUNT(1) AS BB_COUNT
FROM BLOGS BB
GROUP BY BB.USER_ID
) B
ON B.USER_ID = U.ID
LEFT JOIN
(
SELECT
CC.USER_ID
,COUNT(1) AS CC_COUNT
FROM COMMENTS CC
GROUP BY CC.USER_ID
) C
ON C.USER_ID = U.ID
That may or may not be the most efficient way but in my experience it works pretty well and it's simple to understand. It all depends a lot on the number of rows in the tables and indexes etc. Usually the idea is to narrow down rows returned as fast as possible. In this case you'll have two sub queries but they'll end up with only as many rows as you have users basically.
Another thing to note, this will return all users, period. That may not be what you want. You might want only a subset of users. If so this inner select may not be the most efficient because you're doing calculations on users that may not be in the final result, wasting time. However I may be getting off topic.
I agree with the comment that states the table design is not really well constructed yet for you to achieve the counts you want you will need to do subqueries like this:
SELECT
(SELECT count(1) from CarType where user_id=username) as countType,
(SELECT count(1) from CarColor where user_id=username) as countColor,
username from (
SELECT username from Users
) a
As a suggestion for design:
Table Users
Table Cars
Table Colors
Then you have a Relationship table where you have user_id, car_id, color_id
This would be the proper table design for this structure
I have a 2 tables:
People table:
id | name | date
1 | Mika | 2013
2 | Rose | 2015
Work table:
id | user_id | work_name | registers
1 | 1 | rugby | 10
2 | 1 | golf | 3
I use this query to join tables:
SELECT * FROM work INNER JOIN people ON work.user_id = people.id WHERE work_name= :work_name
This work it, but I get duplicate column ID and in php when I try to print the camp id, this show the last column id..
I try with this but dont work it:
SELECT *, id AS 'work_id'
FROM work
INNER JOIN people ON work.user_id = people.id
WHERE work_name= :work_name
That is because you are using * in the SELECT that mean will bring all the fields from boths tables instead you can use
SELECT work.id as work_id,
work.name as work_name,
work.date as work_date,
people.id as people_id,
people.name as people_name,
people.date as people_date
Try to manually list all of columns you need to display, for instance
SELECT w.user_id, w.work_name, w.registers, p.name, p.date FROM work as w INNER JOIN people as p ON work.user_id = people.id WHERE work_name= :work_name
I think I have a somewhat trivial question but I can't figure out how this works. I have the following Companies and Products tables with a simple Many-To-Many relationship.
How would I have to extend this query, so that the results just contains let's say all companies which have products with id 1 AND 2?
I tried adding wheres and havings wherever I could imagine but all i could get was all companies which have products with id x (without the additional and)
Companies Table
id | name
-----------------
1 | Company 1
2 | Company 2
3 | Company 3
Companies_Products Table
id | product_id | company_id
----------------------------
1 | 1 | 1
2 | 2 | 1
3 | 3 | 1
4 | 1 | 2
5 | 1 | 3
6 | 2 | 3
Products Table
id | name
-----------------
1 | Product A
2 | Product B
3 | Product C
Statement
SELECT companies.name,
companies.id AS company_id,
products.id AS product_id
FROM companies
LEFT JOIN company_products
ON companies.id = company_products.company_id
INNER JOIN products
ON company_products.product_id = products.id
If you want ALL companies with associated products 1 and 2, you can write this query:
SELECT c.name,
c.id AS company_id
FROM companies c
WHERE (SELECT COUNT(*)
FROM company_products cp
WHERE cp.company_id = c.id
AND cp.product_id in ('1', '2')
) = 2
Go to Sql Fiddle
If you want to know informations about associated product in the main query so you must use a join in addition of existing query.
Maybe you could using the following subquery in your query:
SELECT company_id, count(*) as no_companies
FROM Companies_Products
WHERE product_id IN (1, 2)
HAVING count(*) = 2
(In this case company an product must be coupled only once.) It returns all the company_ids with product 1 and 2.
There always some discussion about subquery's and performance, but I don't think you will notice.
You could make this function flexible by using a array.
pseudo code:
$parameter = array(1, 2);
...
WHERE product_id IN $parameter
HAVING count(*) = count($parameter)
Please say so if you need more help.
To simplify my problem: Let's say I have 3 tables.
Rooms People Things
-------- -------- --------
id| name id | name | fk_rooms id | name | fk_rooms
----------- --------------------- ---------------------
1 | kitchen 1 | John | 1 1 | TV | 2
2 | bedroom 2 | Mary | 2 2 | bed | 2
3 | Andy | 1 3 | sink | 1
4 | Laura| 1
Now I'm doing something like:
SELECT r.name AS room_name, p.name AS name, t.name AS thing FROM Rooms r
LEFT JOIN People p ON p.fk_rooms = r.id
LEFT JOIN Things t ON t.fk_rooms = r.id
which in my case works perfectly except for a few that have many to one relationship with the "Rooms" table. So instead of new rows in the result set holding the different names for "People" and "Things" in relation to the "Rooms" table, I would like to receive only two rows:
1. kitchen, John, Andy, Laura, sink
2. bedroom, Mary, TV, bed
A GROUP BY on r.id will only select one row from each table. Any help is highly appreciated!
Here it is what you're looking for:
SELECT r.name AS room_name,
GROUP_CONCAT(p.name separator ',') AS people_name,
GROUP_CONCAT(t.name separator ',') AS things
FROM Rooms r
LEFT JOIN People p ON p.fk_rooms = r.id
LEFT JOIN Things t ON t.fk_rooms = r.id
GROUP BY r.id
Yes, you need the group by clause, and you need to use the GROUP_CONCAT function. You should group your results by People.fk_rooms and Thing.fk_rooms.
Maybe you could use two different queries: The first will result the join of Rooms and People, grouped by fk_rooms, having selected three columns, they are being RoomsID, RoomName, People, while the second will result the join of Rooms and Thing, grouped by fk_rooms, having selected three columns, they are being RoomID, RoomName, Things. In your query you name these selections as t1 and t2 and join t1 and t2 by RoomsID, select t1.RoomName, t1.People, t2.Things.
Good luck.