How to Order by in Nested Query? - sql

I have a table like this :
MY_TABLE
Id Name Brand Date
-- ---- ----- ------
1 ABC 1 18-09-2019
2 XYZ 1 19-09-2019
3 MNO 1 18-09-2019
4 ABC 1 19-09-2019
5 PQR 2 17-06-2020
6 MNO 1 19-03-2019
7 ABC 2 19-09-2019
I want to write a query in such a way that first i need to sort based on Date. Based on that result i have to sort count(Name) for a brand.Like this
ID Name Count(Name) Brand date
--- ---- ---------- ------ -----
1 ABC 2 1 19-09-2019
2 XYZ 1 1 19-09-2019 // Eventhough count is less it came second because 19-09-2019 is latest than 19-03-2019
3 MNO 2 1 18-09-2019

You can group by name and aggregate:
select
name,
count(*) counter,
max(brand) brand,
max(trunc(datee)) maxdate
from my_table
where brand = 1
group by name
order by maxdate desc, counter desc
I used trunc(datee) because your dates (as we found out) contain a time part.
See the demo.
Results:
> NAME | COUNTER | BRAND | MAXDATE
> :--- | ------: | ----: | :--------
> ABC | 2 | 1 | 19-SEP-19
> XYZ | 1 | 1 | 19-SEP-19
> MNO | 2 | 1 | 18-SEP-19

You have to first create the count using a group by and then sort by using order by which supports multiple fields (comma separated). And once you give an alias to the column with the count you an use this alias in the order by:
select name, brand, min(date), count(name) as NumRecords, min(id) as id
from my_table
group by name, brand
order by date, NumRecords
I added the min(id) because it seems that you are trying to show the minimum id.

I think you want to select one row per name/brand pair based on the highest count on the most recent date:
select t.*
from (select t.*,
row_number() over (partition by name, brand order by date desc, count desc) as seqnum
from t
) t
where seqnum = 1;

SELECT name, COUNT(*) BrandCount, MAX(brand) brand, date from my_table
ORDER BY date
You may use [ ASC | DESC ] in the end depending upon requirement.
Similar query.
Hope it sorts

Is this what you want?
SELECT MIN(ID), Name,
Count(Name), Brand, MIN(date) as
Date_MIN
From Table where Brand =1 Group by
Brand,Name
Order by Date_MIN desc,Count(Name)
Desc;
;

Related

Order By Id and Limit Offset By Id from a table

I have an issue similar to the following query:
select name, number, id
from tableName
order by id
limit 10 offset 5
But in this case I only take the 10 elements from the group with offset 5
Is there a way to set limit and offset by id?
For example if I have a set:
|------------------------------------|---|---------------------------------------|
| Ana | 1 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jana | 2 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jan | 3 | 589d0011-ef54-4708-a64a-f85228149651 |
| Joe | 2 | 64ed0011-ef54-4708-a64a-f85228149651 |
and if I have skip 1 I should get
|------------------------------------|---|---------------------------------------|
| Jana | 2 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jan | 3 | 589d0011-ef54-4708-a64a-f85228149651 |
I think that you want to filter by row_number():
select name, number, id
from (
select t.*, row_number() over(partition by name order by id) rn
from mytable t
) t
where
rn >= :number_of_records_per_group_to_skip
and rn < :number_of_records_per_group_to_skip + :number_of_records_per_group_to_keep
The query ranks records by id withing groups of records having the same name, and then filters using two parameters:
:number_of_records_per_group_to_skip: how many records per group should be skipped
:number_of_records_per_group_to_skip: how many records per group should be kept (after skipping :number_of_records_per_group_to_skip records)
This might not be the answer you are looking for but it gives you the results your example shows:
select name, number, id
from (
select * from tableName
order by id
limit 3 offset 0
) d
where id > 1;
Best regards,
Bjarni

get the id based on condition in group by

I'm trying to create a sql query to merge rows where there are equal dates. the idea is to do this based on the highest amount of hours, so that i in the end gets the corresponding id for each date with the highest amount of hours. i've been trying to do with a simple group by, but does not seem to work, since i CANT just put a aggregate function on id column, since it should be based the hours condition
+------+-------+--------------------------------------+
| id | date | hours |
+------+-------+--------------------------------------+
| 1 | 2012-01-01 | 37 |
| 2 | 2012-01-01 | 10 |
| 3 | 2012-01-01 | 5 |
| 4 | 2012-01-02 | 37 |
+------+-------+--------------------------------------+
desired result
+------+-------+--------------------------------------+
| id | date | hours |
+------+-------+--------------------------------------+
| 1 | 2012-01-01 | 37 |
| 4 | 2012-01-02 | 37 |
+------+-------+--------------------------------------+
If you want exactly one row -- even if there are ties -- then use row_number():
select t.*
from (select t.*, row_number() over (partition by date order by hours desc) as seqnum
from t
) t
where seqnum = 1;
Ironically, both Postgres and Oracle (the original tags) have what I would consider to be better ways of doing this, but they are quite different.
Postgres:
select distinct on (date) t.*
from t
order by date, hours desc;
Oracle:
select date, max(hours) as hours,
max(id) keep (dense_rank first over order by hours desc) as id
from t
group by date;
Here's one approach using row_number:
select id, dt, hours
from (
select id, dt, hours, row_number() over (partition by dt order by hours desc) rn
from yourtable
) t
where rn = 1
You can use subquery with correlation approach :
select t.*
from table t
where id = (select t1.id
from table t1
where t1.date = t.date
order by t1.hours desc
limit 1);
In Oracle you can use fetch first 1 row only in subquery instead of LIMIT clause.

How can you choose the line with the higher value in SQL?

Let's say we have this table:
----------
number |name| price
----------
1 | a | 2
----------
1 | b | 4
----------
2 | c | 4
----------
3 | d | 6
----------
I want to pick the numbers and their highest price. So how can i select only the line with number 1, which has the higher price and not show the other one?. My problem is the second column.
Easy: GROUP BY
select number, max(price)
from your_table
group by number;
If you have more columns and you want to select the row with max price, use this:
select *
from (select
t.*,
row_number() over (partition by number order by price desc) rn
from your_table t) t
where rn = 1;
In Postgres, the most efficient way to get the row with the highest price for each number is to use distinct on:
select distinct on (number) t.*
from t
order by number, price desc;

sql aggregate data

this is not a specific dbms question, but a generic sql problem.
i have this dataset
userid | objecteid| count
--------------------------
1 | 1 | 12
1 | 2 | 15
1 | 3 | 6
2 | 4 | 30
2 | 1 | 1
2 | 5 | 9
with one query i need to find: for each user, the object with the maximum count
looking for a result like this:
userid | objecteid| count
--------------------------
1 | 2 | 15
2 | 4 | 30
because the object 2 has the max count for user 1 and the object 4 has the max count for user 2
This can easily be solved using window functions.
The following is standard ANSI SQL:
select userid, objecteid, "count"
from (
select userid, objecteid, "count",
max("count") over (partition by userid) as max_cnt
from the_table
) t
where "count" = max_cnt;
If there are two objects with the same count, both will be returned.
Alternatively this can also be done using row_number() instead:
select userid, objecteid, "count"
from (
select userid, objecteid, "count",
row_number() over (partition by userid order by "count" desc) as rn
from the_table
) t
where rn = 1;
Unlike the first query, this will only pick one row if a user has more than one object with the same count. If you want those duplicates returned, use dense_rank() instead of row_number()
SQLFiddle: http://sqlfiddle.com/#!15/f02a9/1
try this
Select * from tableName
where count in (
Select Max(count)
from tableName
group by userid
)

get status on specific date

Table structure: (database is oracle 12c)
CUSTOMER_ID | STATUS | STATUS_FROM_DATE
101 | ABC | 10-01-2015
101 | PQR | 27-02-2015
101 | LMN | 04-08-2015
101 | ABC | 08-09-2015
Question: How to get status of customer for specific date from above table?
For example:
CUSTOMER_ID | Input Date | Expected Output
101 | 15-01-2015 | ABC
101 | 27-02-2015 | PQR
101 | 28-02-2015 | PQR
101 | 10-09-2015 | ABC
In above example,
ABC was the status of customer on 15-01-2015, because this is set on 10-01-2015 and not changed till 27-02-2015.
PQR was the status of customer on 28-02-2015, because this is set on 27-02-2015 and not changed till 04-08-2015.
You can use lead analytic function to get the end of interval. Then just search using between.
select * from
(
select
customer_id,
status,
status_from_date,
nvl(lead(status_from_date) over (partition by customer_id order by status_from_date)-1,
to_date('2099','yyyy')
)as end_date
from your_table
)
where your_date_here between status_start_date and end_date
Using newly introduced row limiting clause in Oracle 12c,
select <your input date> as date_, expected_status
from myt
where status_date <= <your input date>
order by status_date desc
fetch first 1 rows only;
SQL FIDDLE DEMO
with ranges as (
select t.*,
lead(STATUS_FROM_DATE,1, (select sysdate from dual))
over (partition by CUSTOMER_ID order by STATUS_FROM_DATE) as status_change
from Table1 t
)
select r.status, s."Date", s."Expected Output"
from ranges r
inner join TestStatus s
on s."Date" < r.status_change
and s."Date" >= r.STATUS_FROM_DATE;