Why this query wont work? Is it beacause combinaton of order by and group by?
One table is with adverts, other with subscriptions, third is with services, and fourth is many to many relation between services and locations (location is position where advert should be shown).
What i want is to order adverts stored in adverts table having location 2 first, then those who don't have location defined and then with location 1 (this order is generated programmicaly)
adverts table:
id, name, subscription_id
subscriptions table:
subscription_id, service_id, date, paid etc...
service_locations table:
service_id, location_id
as you can se there is fourth table in this case, but it is unimportant
The query:
select adverts.id, GROUP_CONCAT(service_locations.location_id) AS locations from adverts
left join subscriptions
on adverts.subscription_id = subscriptions.id
left join service_locations
on service_locations.service_id = subscriptions.service_id
group by adverts.id
order by case service_locations.location_id
when 2 then 1
when 1 then 3
else 2
end
Expected results:
+----+-----------+
| id | locations |
+----+-----------+
| 1 | 2 |
| 3 | 1,2 |
| 2 | null |
+----+-----------+
What i actually get (the third in row has location 2 but it is placed after null):
+----+-----------+
| id | locations |
+----+-----------+
| 1 | 2 |
| 2 | null |
| 3 | 1,2 |
+----+-----------+
When you use group by, all columns not in the group by should have aggregation functions. So, I think you intend something like this:
select a.id, GROUP_CONCAT(sl.location_id) AS locations
from adverts a left join
subscriptions s
on a.subscription_id = s.id left join
service_locations sl
on sl.service_id = s.service_id
group by a.id
order by max(case sl.location_id
when 2 then 1
when 1 then 3
else 2
end);
I'm not sure if max() is what you really need, but you do need an aggregation function. This specifically produces the output in the question:
order by (case min(sl.location_id)
when 2 then 1
when 1 then 2
else 3
end);
I have found a solution, order by must be executed before group by, which is not a default behaivor, more about that behaivour here: https://stackoverflow.com/a/14771322/4329156) (a subquery must be used)
So, query should look like
select *, GROUP_CONCAT(location_id) as locations from (
select adverts.id AS id, service_locations.location_id AS location_id from adverts
left join subscriptions
on adverts.subscription_id = subscriptions.id
left join service_locations
on service_locations.service_id = subscriptions.service_id
order by case service_locations.location_id
when 2 then 1
when 1 then 3
else 2
end
) as table
group by table.id
order by case table.location_id
when 2 then 1
when 1 then 3
else 2
end
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 have a table like this (simplified version):
+------+-------+-----+--------------+-----+
| id | name | age | company.name | ...
+------+-------+-----+--------------------+
| 1 | Adam | 21 | Google | ...
| 3 | Peter | 20 | Apple | ...
| 2 | Bob | 20 | Microsoft | ...
| 9 | Alice | 18 | Google | ...
+------+-------+-----+--------------------+
I need groups data with counting rows by any one column. And I need to get first row in each group. User select which column will be used to group.
If user select column age to group then results:
+------+------------+-------+
| id | group_name | count |
+------+------------+-------+
| 9 | 18 | 1 |
+------+------------+-------+
| 2 | 20 | 2 |
+------+------------+-------+
| 1 | 21 | 1 |
+------+------------+-------+
Column to group may be numeric or string.
Currently I does it by this query:
SELECT id, group_name, users_name, count(id) as count FROM (
SELECT persons.id as id, company.type as group_name, users.name as users_name
FROM persons
LEFT JOIN company on company.id = persons.company_id
LEFT JOIN position on position.id=persons.position_id
...
LEFT JOIN source on source.id=persons.source_id
WHERE ...
ORDER BY if(company.type = '' or company.type is null,1,0) ASC,
company.type ASC, IF(persons.status = '' or persons.status is null,1,0) ASC,
persons.status ASC, persons.id
) t1 GROUP BY group_name
but with new version mysql this SQL stoped works I think that order is ignored in sub-select.
I know that similar topics was wroted, but proposed solutions not working with my query. I have to join many tables, add multiple conditions and use cascade order and then select first row from each group. I will be very happy if solution will be optimised for performace.
---- EDIT ----
Proposed solution:
SQL select only rows with max value on a column
which suggest to use MAX() and GROUP BY not working well. For two reason
If grouped column include string, then query return not first row, but last row in each group.
If my dataset has a cascade order, I can not use MAX in a few columns at the same time.
I created sqlfiddle which include exact example.
http://sqlfiddle.com/#!9/23225d/11/0
-- EXAMPLE 1 - Group by string
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.name ASC, company.id ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.name, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.name
ORDER BY company.name ASC, persons.id ASC;
-- The results will be:
-- |ID | NAME | COUNT|
-- |1 | Google | 2 |
-- |3 | Microsoft| 3 |
-- EXAMPLE 2 - Cascade order
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.type ASC, persons.status ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.type, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.type
ORDER BY company.type ASC, persons.status ASC;
-- The results will be:
-- |ID | NAME| COUNT|
-- |3 | 1 | 2 |
-- |2 | 2 | 3 |
Just change MAX() to MIN() to get the first row instead of the last row in each group.
To get the extreme values of cascading columns, see SQL : Using GROUP BY and MAX on multiple columns. Use that in the subquery part of the query to get the row containing those extremes, as in SQL select only rows with max value on a column.
So the form of the full query is:
SELECT t1.id, t1.grouped_column, t2.count
FROM yourTable AS t
JOIN (SELECT t3.grouped_column, t3.order_column1, MIN(t4.order_column2) AS order_column2, SUM(t3.count) AS count
FROM (SELECT grouped_column, MIN(order_column1) AS order_column1, COUNT(*) AS count
FROM yourTable
GROUP BY grouped_column) AS t3
JOIN yourTable AS t4
ON t3.grouped_column = t4.grouped_column AND t3.order_column1 = t4.order_column1
GROUP BY t4.grouped_column, t4.order_column1) AS t2
ON t1.grouped_column = t2.grouped_column AND t1.ordered_column1 = t2.order_column1 AND t1.order_column2 = t2.order_column2
Since you want to operate on a join, I suggest you define a view that uses the join. Then you can use that view in place of yourTable in the above query.
I have two column one column associated with another...
Table:base_data
id |---name----|-----des
1 | some name1 | The description1
2 | some name2 | The description2
Table: photos
id |---p_id----|-----photo
1 | 1 | img1s.jpg
2 | 1 | img1w.jpg
3 | 2 | img2.jpg
4 | 2 | img14.jpg
5 | 2 | img15.jpg
I want to select all data from table 1(base_data) and one row from associated row from photos: table how can I do that ????
I don't want to select by greatest n per group I want to select all data from the first table and only one row of the second table which matches with the first table row id, just first match not other.
The Result I want...
id |---name----|---des----|---p_id----|---photo----|
1 | some name |the des..1| 1 | img1s.jpg|
2 | some name |the des..2| 2 | img2.jpg|
I suppose you want to associate base_data with the first photo taken, which should be the one with the lowest photos.id. In MySQL, you could write this as follows: Create an intermediate query which gives - for any p_id - the corresponding record with the lowest id. Then, left join base_data with this intermediate query result. Hope there are not to many typos in it :-) :
select b.id, p2.photo
from base_data b left join
(select p.photo, p.p_id, min(id) from photos p group by p.p_id) p2 on b.id = p2.p_id
If you want the alphanumerically lowest photo name, in MySQL you can do this:
select
t1.*,
t2.photo
from
base_data as t1
left join (
select
p_id,
min(photo) as photo
from
photos
group by
p_id
) as t2 on t2.p_id = t1.id;
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.
I have a table that stores product groups.
table "products_groups"
id | id_group | id_product
============================
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
6 | 3 | 2
I need a SQL query that will find the id_group that contains all id_product's that are in the given array.
product_ids = array(1); // should return no results
product_ids = array(1,2); // should only return id_group 2
product_ids = array(1,2,3); // should only return id_group 1
product_ids = array(1,2,3,4); // should return no results
I played/searched around, ended up stuck at
SELECT p1.id_group
FROM products_groups p1, products_groups p2
WHERE p1.id <> p2.id
AND p1.id_group = p2.id_group
AND (
p1.id_product = 1
OR p1.id_product = 2
OR p1.id_product = 3
)
But it obviously is not giving me the result I am looking for. I don't know if I am thinking too simple or too complex.
Notes: Of course the id_product values will be dynamically generated in the SQL. It will eventually be used with PHP/Codeigniter
Background info: Each product has a price, but products can be in a product group which has a package price. That is why I need to know for each order if the products are in a group.
This problem is called Relational Division
SELECT id_group
FROM products_groups
WHERE id_product IN (1,2)
GROUP BY id_group
HAVING COUNT(*) = 2
SQL of Relational Division
or something like this,
SELECT id_group
FROM products_groups
GROUP BY id_group
HAVING SUM(id_product IN (1,2)) = COUNT(*) AND
COUNT(*) = 2
SQLFiddle Demo