I have TableA which contains:
ID | CATEGORY | NAME
-------------------------
1 | A | NAME01
2 | B | NAME02
3 | C | NAME03
4 | D | NAME04
5 | B | NAME05
So how can I count which of the Categorys in my table I have the most?
We can see it is the Category B, but in my real table I have more than 200 records added.
In the table I have 5 different Categories.
It seems like you can just use an aggregate function (count()) and a group by:
select category, count(category) Total
from TableA
group by category
order by Total desc
See SQL Fiddle with Demo
If you then want to return only the record with the most, you can add a LIMIT to it:
select category, count(category) Total
from TableA
group by category
order by Total Desc
limit 1
See SQL Fiddle with Demo
Related
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 have the following MySQL DB structure:
table sales_order - id, name, ...
id | name
------------------
1 | Order Test
table sales_order_item - id, order_id, name, amount_dispatched ...
id | order_id | name | amount_dispatched
------------------------------------------
1 | 1 | Item 1 | 5
2 | 1 | Item 2 | 10
table sales_order_item_invoice - id, item_id, amount, ...
id | item_id | amount
---------------------
1 | 1 | 3
2 | 2 | 5
3 | 2 | 5
These three tables are in chain via the foreign keys. Table "invoice" can have more rows for one row in "item". Table "item" can have more rows for one row in "order".
Now, I need to create SQL query that returns all rows from table sales_order and appends there some data from the other tables - amount_dispatched and amount_invoiced:
dispatched = sum of all order's items' amount_dispatched
invoiced = sum of all invoices' amount (or 0 if no invoice exists)
Such query seems to be straightforward:
SELECT
`sales_order`.*,
SUM(`sales_order_item`.`amount_dispatched`) AS dispatched,
SUM(`sales_order_item_invoice`.`amount`) AS invoiced,
FROM `sales_order`
LEFT JOIN `sales_order_item` ON `sales_order`.`id` = `sales_order_item`.`order_id`
LEFT JOIN `sales_order_item_invoice` ON `sales_order_item`.`id` =`sales_order_item_invoice`.`item_id`
GROUP BY `sales_order`.`id`
The result contains all orders - ok
The result contains sum of invoices amount - ok
The result of "amount_dispatched" is invalid - if the item has more rows in item_invoice, the item's amount is summed several times, so for the example above, I get:
id | name | dispatched | invoiced
---------------------------------------
1 | Order Test | 25 | 13
Amount_dispatched is 25, but I would expect it to be 15.
Any idea how to correct my SQL query?
Thank you.
Firstly, use subquery do aggregation for invoice amount in sales_order_item_invoice, then left join.
SELECT
`sales_order`.*,
SUM(`sales_order_item`.`amount_dispatched`) AS dispatched,
SUM(t.`amount`) AS invoiced
FROM `sales_order`
LEFT JOIN `sales_order_item` ON `sales_order`.`id` = `sales_order_item`.`order_id`
LEFT JOIN (
SELECT item_id, SUM(amount) AS amount
FROM `sales_order_item_invoice`
GROUP BY item_id
) t ON `sales_order_item`.`id` = t.`item_id`
GROUP BY `sales_order`.`id`
I want to show first two top voted Posts then others sorted by id
This is table
+----+-------+--------------+--------+
| Id | Name | Post | Votes |
+====+=======+==============+========+
| 1 | John | John's msg | -6 |
| 2 |Joseph |Joseph's msg | 8 |
| 3 | Ivan | Ivan's msg | 3 |
| 4 |Natalie|Natalie's msg | 10 |
+----+-------+--------------+--------+
After query result should be:
+----+-------+--------------+--------+
| Id | Name | Post | Votes |
+====+=======+==============+========+
| 4 |Natalie|Natalie's msg | 10 |
| 2 |Joseph |Joseph's msg | 8 |
-----------------------------------------------
| 1 | John | John's msg | -6 |
| 3 | Ivan | Ivan's msg | 3 |
+----+-------+--------------+--------+
I have 1 solution but i feel like there is better and faster way to do it.
I run 2 queries, one to get top 2, then second to get others:
SELECT * FROM table order by Votes desc LIMIT 2
SELECT * FROM table order by Id desc
And then in PHP i make sure that i show 1st query as it is, and on displaying 2nd query i remove entry's that are in 1st query so they don't double.
Can this be done in single query to select first two top voted, then others?
You would have to use subqueries or union - meaning you have a single outer query, which contains multiple queries inside. I would simply retrieve the IDs from the first query and add a id not in (...) criterion to the where clause of the 2nd query - thus filtering out the posts retrieved in the first query:
SELECT * FROM table WHERE Id NOT IN (...) ORDER BY Id DESC
With union the query would look like as follows:
(SELECT table.*, 1 as o FROM table order by Votes desc LIMIT 2)
UNION
(SELECT table.*, 0 FROM table
WHERE Id NOT IN (SELECT Id FROM table order by Votes desc LIMIT 2))
ORDER BY o DESC, if(o=1,Votes,Id) DESC
As you can see, it wraps 3 queries into one and has a more complicated ordering as well because in union the order of the records retrieved is not guaranteed.
Two simple queries seem to be a lot more efficient to me in this particular case.
There could be different ways to write a query that returns the rows in the order you want. My solution is this:
select
table.*
from
table left join (select id from table order by votes desc limit 2) l
on table.id = l.id
order by
case when l.id is not null then votes end desc,
tp.id
the subquery will return the first two id ordered by votes desc, the join will succeed whenever the row is one of the first two otherwise l.id will be null instead.
The order by will order by number of votes desc whenever the row is the first or the second (=l.id is not null), when l.id is null it will put the rows at the bottom and order by id instead.
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
I have two tables, one of items, and one of users who have flagged the items. Here is an example:
items: flags:
item_id | item_name | owner_id item_id | flagged_by
------------------------------ --------------------
1 | foo | 1 1 | 2
2 | bar | 2 2 | 4
3 | baz | 2 2 | 7
2 | 7
I want to select the information from the item table about all the items that are in the flag table, ordered by the number of flags. So for the above example, my desired output would be
item_id | item_name | owner_id
------------------------------
2 | bar | 2
1 | foo | 1
The query I have right now is select * from items where id in (select item_id from flags group by item_id order by count(*) desc);
I know that the inner query works correctly (returns all the IDs in the correct order) but when I run the overall query, I just get the items in order of item id. How do I fix my query?
You are ordering the subquery only currently, which doesn't have an effect on the order of the outer query. If you join the tables rather than using a subquery, you should be able to apply an order to the whole query:
select i.*
from items i
join flags f on i.item_id = f.item_id
group by i.item_id
order by count(f.item_id) desc
Demo: http://www.sqlfiddle.com/#!2/f141b/2