Order By 2 columns - sql

I have this table and .
id | item_id | created_at
-----+-----------+------------
1 | Apple | 2017-03-21
2 | Grape | 2017-03-23
3 | Grape | 2017-03-24
4 | Apple | 2017-03-25
I want to order by created_at and also at the same time order by item_id like this:
id | item_id | created_at
-----+-----------+------------
4 | Apple | 2017-03-25
1 | Apple | 2017-03-21
3 | Grape | 2017-03-24
2 | Grape | 2017-03-23
So if i add a new row for item_id: Grape my new results should be like this:
id | item_id | created_at
-----+-----------+------------
5 | Grape | 2017-03-28 (NEW)
3 | Grape | 2017-03-24
2 | Grape | 2017-03-23
4 | Apple | 2017-03-25
1 | Apple | 2017-03-21
and then if i add new row for item_id: Apple it should be like this:
id | item_id | created_at
-----+-----------+------------
6 | Apple | 2017-03-28 (NEW)
4 | Apple | 2017-03-25
1 | Apple | 2017-03-21
5 | Grape | 2017-03-27
3 | Grape | 2017-03-24
2 | Grape | 2017-03-23
...So it orders by the latest created_at and show the other rows with the same item_id below it
I have tried ORDER BY created_at, item_id DESC but it does not works and give me this instead:
id | item_id | created_at
-----+-----------+------------
6 | Apple | 2017-03-28
5 | Grape | 2017-03-27
4 | Apple | 2017-03-25
3 | Grape | 2017-03-24
2 | Grape | 2017-03-23
1 | Apple | 2017-03-21

SQLFiddle: PostgreSQL
WITH grouped AS (
select item_id, max(created_at) as max_dt
from tbl
group by item_id
)
SELECT tbl.*
FROM grouped
LEFT JOIN tbl USING (item_id)
ORDER BY grouped.max_dt desc,
grouped.item_id, -- important if apple and grape both have same max dt
tbl.created_at desc;
subquery to get the most recent dates by item (the item grouping)
left join those results on the actual table to get desired records

You need to first identify your first rule which is get the max created_at per item_id, therefore the subquery with the item_id grouped and a row_number() added so it can know which fruit cames first. Then just join with your table.
SELECT tt.id, tt.item_id, tt.created_at
FROM test_table tt
INNER JOIN
(SELECT item_id,
MAX(created_at),
ROW_NUMBER() OVER (ORDER BY MAX(created_at) DESC) rn
FROM test_table
GROUP BY item_id
ORDER BY 3) ord
ON tt.item_id = ord.item_id
ORDER BY ord.rn, tt.created_at DESC;

For mysql, try this:
select item.*
from item
left join (
select item_id, max(created_at) maxdate
from item
group by item_id
) t on item.item_id = t.item_id
order by t.maxdate desc, item.created_at desc
Demo1 in SQLFiddle
Demo2 in SQLFiddle

Here is a mysql version
set #ordr = 0;
select t.*
from tbl t
join (
select o.item_id, o.created_at, #ordr := #ordr + 1 as `order`
from (
select tbl.item_id, max(tbl.created_at) as created_at
from tbl
group by tbl.item_id
order by created_at desc
) as o
) sub
on t.item_id = sub.item_id
order by sub.`order`, sub.created_at desc

Please try -
SELECT id,
item_ID,
created_at
FROM tblOrders
ORDER BY item_ID,
item_ID = ( SELECT item_ID
FROM tblOrders
WHERE id = ( SELECT MAX( id ) FROM tblOrders ) ),
created_at;

Related

Greatest count for each customer in PostgreSQL

customer | category | count
------------+---------------+-------
4846 | Vegetables | 1
1687 | Fast-Food | 7
2654 | Drink | 2
2654 | Vegetables | 3
1597 | Vegetables | 1
4846 | Drink | 2
2654 | Fast-Food | 1
1597 | Drink | 6
1597 | Snack | 3
how can i select the category which has greatest count for each customer for this table?
This is called the mode. You can use distinct on:
select distinct on (customer) t.*
from t
order by customer, count desc;
You can use window function row_number().
select
customer,
category,
count
from
(
select
*,
row_number() over (partition by customer order by count desc) as rnk
from yourTable
) val
where rnk = 1
Simple code for you try:
SELECT c.*
FROM (SELECT customer, max(count) as max_count
FROM customers
GROUP BY customer) as max_count_table
JOIN customers as c on max_count_table.customer = c.customer and max_count_table.max_count = c.count
Result:

Getting date, and count of unique customers when first order was placed

I have a table called orders that looks like this:
+--------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| memberid | int(11) | YES | | NULL | |
| deliverydate | date | YES | | NULL | |
+--------------+---------+------+-----+---------+-------+
And that contains the following data:
+------+----------+--------------+
| id | memberid | deliverydate |
+------+----------+--------------+
| 1 | 991 | 2019-10-25 |
| 2 | 991 | 2019-10-26 |
| 3 | 992 | 2019-10-25 |
| 4 | 992 | 2019-10-25 |
| 5 | 993 | 2019-10-24 |
| 7 | 994 | 2019-10-21 |
| 6 | 994 | 2019-10-26 |
| 8 | 995 | 2019-10-26 |
+------+----------+--------------+
I would like a result set returning each unique date, and a separate column showing how many customers that placed their first order that day.
I'm having problems with querying this the right way, especially when the data consists of multiple orders the same day from the same customer.
My approach has been to
Get all unique memberids that placed an order during the time period I want to look at
Filter out the ones that placed their first order during the period by comparing the memberids that has placed an order before the timeperiod
Grouping by delivery date, and counting all unique memberids (but this obviously counts unique memberids each day individually!)
Here's the corresponding SQL:
SELECT deliverydate,COUNT(DISTINCT memberid) FROM orders
WHERE
MemberId IN (SELECT DISTINCT memberid FROM orders WHERE deliverydate BETWEEN '2019-10-25' AND '2019-10-26')
AND NOT
MemberId In (SELECT DISTINCT memberid FROM orders WHERE deliverydate < '2019-10-25')
GROUP BY deliverydate
ORDER BY deliverydate ASC;
But this results in the following with the above data:
+--------------+--------------------------+
| deliverydate | COUNT(DISTINCT memberid) |
+--------------+--------------------------+
| 2019-10-25 | 2 |
| 2019-10-26 | 2 |
+--------------+--------------------------+
The count for 2019-10-26 should be 1.
Appreciate any help :)
You can aggregate twice:
select first_deliverydate, count(*) cnt
from (
select min(deliverydate) first_deliverydate
from orders
group by memberid
) t
group by first_deliverydate
order by first_deliverydate
The subquery gives you the first order data of each member, then the outer query aggregates and counts by first order date.
This demo on DB Fiddle with your sample data returns:
first_deliverydate | cnt
:----------------- | --:
2019-10-21 | 1
2019-10-24 | 1
2019-10-25 | 2
2019-10-26 | 1
In MySQL 8.0, This can also be achieved with window functions:
select deliverydate first_deliverydate, count(*) cnt
from (
select deliverydate, row_number() over(partition by memberid order by deliverydate) rn
from orders
) t
where rn = 1
group by deliverydate
order by deliverydate
Demo on DB Fiddle
you have first to figure out when was the first delivery date:
SELECT firstdeliverydate,COUNT(DISTINCT memberid) FROM (
select memberid, min(deliverydate) as firstdeliverydate
from orders
WHERE
MemberId IN (SELECT DISTINCT memberid FROM orders WHERE deliverydate BETWEEN '2019-10-25' AND '2019-10-26')
AND NOT
MemberId In (SELECT DISTINCT memberid FROM orders WHERE deliverydate < '2019-10-25')
group by memberid)
t1
group by firstdeliverydate
Get the first order of each customer with NOT EXISTS and then GROUP BY deliverydate to count the distinct customers who placed their order:
select o.deliverydate, count(distinct o.memberid) counter
from orders o
where not exists (
select 1 from orders
where memberid = o.memberid and deliverydate < o.deliverydate
)
group by o.deliverydate
See the demo.
Results:
| deliverydate | counter |
| ------------------- | ------- |
| 2019-10-21 00:00:00 | 1 |
| 2019-10-24 00:00:00 | 1 |
| 2019-10-25 00:00:00 | 2 |
| 2019-10-26 00:00:00 | 1 |
But if you want results for all the dates in the table including those dates where there where no orders from new customers (so the counter will be 0):
select d.deliverydate, count(distinct o.memberid) counter
from (
select distinct deliverydate
from orders
) d left join orders o
on o.deliverydate = d.deliverydate and not exists (
select 1 from orders
where memberid = o.memberid and deliverydate < o.deliverydate
)
group by d.deliverydate

Sql two table query most duplicated foreign key

I got those two tables sport and student:
First table sport:
|idsport | name |
_______________________
| 1 | bobsled |
| 2 | skating |
| 3 | boarding |
| 4 | iceskating |
| 5 | skiing |
Second table student:
foreign key
|idstudent | name | sport_idsport
__________________________________________
| 1 | john | 3 |
| 2 | pauly | 2 |
| 3 | max | 1 |
| 4 | jane | 2 |
| 5 | nico | 5 |
so far i did this it output which number is mostly inserted, but cant get it to work
with two tables
SELECT sport_idsport
FROM (SELECT sport_idsport FROM student GROUP BY sport_idsport ORDER BY COUNT(*) desc)
WHERE ROWNUM<=1;
I need to output name of most popular sport, in that case it would be skating.
I use oracle sql.
with counter as (
Select sport_idsport,
count(*) as cnt,
dense_rank() over (order by count(*) desc) as rn
from student
group by sport_idsport
)
select s.*, c.cnt
from sport s
join counter c on c.sport_idsport = s.idsport and c.rn = 1;
SQLFiddle example: http://sqlfiddle.com/#!4/b76e21/1
select cnt, sport_idsport from (
select count(*) cnt, sport_idsport
from student
group by sport_idsport
order by count(*) desc
)
where rownum = 1

Oracle ranking columns on multiple fields

I am having some issues with ranking some columns in Oracle. I have two columns I need to rank--a group id and a date.
I want to group the table two ways:
Rank the records in each GROUP_ID by DATETIME (RANK_1)
Rank the GROUP_IDs by their DATETIME, GROUP_ID (RANK_2)
It should look like this:
GROUP_ID | DATE | RANK_1 | RANK_2
----------|------------|-----------|----------
2 | 1/1/2012 | 1 | 1
2 | 1/2/2012 | 2 | 1
2 | 1/4/2012 | 3 | 1
3 | 1/1/2012 | 1 | 2
1 | 1/3/2012 | 1 | 3
I have been able to do the former, but have been unable to figure out the latter.
SELECT group_id,
datetime,
ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY datetime) AS rn,
DENSE_RANK() OVER (ORDER BY group_id) AS rn2
FROM table_1
ORDER BY group_id;
This incorrectly orders the RANK_2 field:
GROUP_ID | DATE | RANK_1 | RANK_2
----------|------------|-----------|----------
1 | 1/3/2012 | 1 | 1
2 | 1/1/2012 | 1 | 2
2 | 1/2/2012 | 2 | 2
2 | 1/4/2012 | 3 | 2
3 | 1/1/2012 | 1 | 3
Assuming you don't have an actual id column in the table, it appears that you want to do the second rank by the earliest date in each group. This will require a nested subquery:
select group_id, datetime, rn,
dense_rank() over (order by EarliestDate, group_id) as rn2
from (SELECT group_id, datetime,
ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY datetime) AS rn,
min(datetime) OVER (partition by group_id) as EarliestDate
FROM table_1
) t
ORDER BY group_id;

Getting the whole row from grouped result

Here's a sample database table :
| ID | ProductID | DateChanged | Price
| 1 | 12 | 2011-11-11 | 93
| 2 | 2 | 2011-11-12 | 12
| 3 | 3 | 2011-11-13 | 25
| 4 | 4 | 2011-11-14 | 17
| 5 | 12 | 2011-11-15 | 97
Basically, what I want to happen is get the latest price of grouped by ProductID.
The result should be like this :
| ID | ProductID | Price
| 2 | 2 | 12
| 3 | 3 | 25
| 4 | 4 | 17
| 5 | 12 | 97
If you notice, the first row is not there because there is a new price for ProductID 12 which is the row of ID 5.
Basically, it should be something like get ID,ProductID and Price grouped by productID where DateChanged is the latest.
SELECT ID, ProductId, Price
FROM
(
SELECT ID, ProductId, Price
, ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY DateChanged DESC) AS rowNumber
FROM yourTable
) AS t
WHERE t.rowNumber = 1
SELECT ID, ProductID,DateChanged, Price
FROM myTable
WHERE ID IN
(
SELECT MAX(ID)
FROM myTable
GROUP BY ProductID
)
select a.id, a.productid, a.price
from mytable a,
(select productid, max(datechanged) as maxdatechanged
from mytable
group by productid) as b
where a.productid = b.productid and a.datechanged = b.maxdatechanged
SELECT ID, ProductId, Price
from myTable A
where DateChanged >= all
(select DateChanged
from myTable B
where B.ID = A.ID);