group by in SQL, improvement of query - sql

I am facing one small issue.
I have a table MY_CHART_TABLE(ID,REASON_CODE,QUANTITY)
101 CompFail 57
101 FitFinish 18
101 CompDamage 16
102 NoFail 57
102 NoFinish 18
103 FullDamage 16
output I want
101 CompFail 57 3
101 FitFinish 18 3
101 CompDamage 16 3
102 NoFail 57 2
102 NoFinish 18 2
103 FullDamage 16 1
I need to store count at the end based on id. how can I do it?
I am using below query
SELECT
id,
reason_code,
quantity,
COUNT(*) OVER (PARTITION BY id )
FROM MY_CHART_TABLE;
Is there any better way to improve the query? can it be done using group by?

You code is fine as is, but if you must use group by, you can use a derived table to obtain the counts before you join it back to the main table.
select a.*, b.counts
from MY_CHART_TABLE a
join (select id, count(*) as counts
from MY_CHART_TABLE
group by id) b on a.id=b.id;
For your problem, I would reiterate that your code is the cleaner and better way of doing this. Note that your code as is will count all the rows per id. If you only need to count distinct rows per id, you can add a group by. Window functions are applied after group by, so this way- you'll be counting only distinct rows per id
select id, reason_code, quantity, count(*) over (partition by id)
from MY_CHART_TABLE
group by id, reason_code, quantity;

You can use CTEs with group by
;with cnts as(
select
id,
[count] = count(*)
from MY_CHART_TABLE
group by id
)
select mct.*, cnts.[count]
from MY_CHART_TABLE mct
join cnts
on cnts.id = mct.id

Related

how to get row value with group by clause using subquery in PostgreSQL in Laravel 8?

This is my table, first I want to get status_exec of each MAX (date_sta) and after that I want to grouped by status_exec and get the COUNT.
id_out_sta
status_exec
date_sta
1
2
2021-11-07
1
1
2021-11-28
1
5
2021-12-07
2
7
2021-04-02
2
2
2021-05-12
2
6
2021-08-07
3
2
2021-08-05
3
5
2021-08-28
4
2
2021-03-15
4
5
2021-04-25
The result I would expect should be the following:
status_exec
COUNT
5
3
6
1
This is my query but it didn't help:
SELECT id_out_sta, status_exec , max(date_sta) as max_date_sta
FROM public.status_exe
join public.order_out on status_exe.id_out_sta = order_out.id_out
group by (id_out_sta);
Please any suggestion, query builder or simple query.
A common solution for this is row_number window function to find the maximum of each group. Using this in a CTE and then aggregating the result:
with s as (
select *,
Row_Number() over(partition by id_out_sta order by max_date_sta desc) rn
from t
)
select status_exec, Count(*) "Count"
from s
where rn=1
group by status_exec
Example DB<>Fiddle
Using DISTINCT ON followed by a subquery:
SELECT status_exec, COUNT(*) AS COUNT
FROM
(
SELECT DISTINCT ON (status_exec) *
FROM public.status_exe
ORDER BY status_exec, max_date_sta DESC
) t
GROUP BY status_exec;
Here is another example by using count window function.
SELECT * FROM (
SELECT DISTINCT ON (id_out_sta)
status_exec,
count(*) over(partition by status_exec)
FROM t
ORDER BY id_out_sta, max_date_sta DESC
) as list
GROUP BY 1,2
Fiddle is here

Keep minimum value of field A and corresponding value of field B in SQL server

Say I have the following table:
Category rank score
a 3 100
a 1 105
a 2 110
b 2 102
b 7 107
b 3 95
I would like to know both the most efficient and the most visually elegant way of getting the lines having the minimum rank for each category.
In my example the result would be
Category rank score
a 1 105
b 2 102
The solutions I came up with seem inefficient and ugly for something that seems quite straightforward.
A typical solution is to use row_number():
select category, rank, score
from (select t.*,
row_number() over (partition by category order by rank) as seqnum
from t
) t
where seqnum = 1;
Whether or not you think this is elegant is a matter of opinion.
Below solution uses the concept of CTE....
with cte as
(
select category, rank, score, ROW_NUMBER() OVER(PARTITION BY category ORDER BY rank ) AS row_num
from t
)
select category, rank, score from cte
where row_num=1

How to select group by 2 columns + id?

I have a problem with data selection using SQL in PostgreSQL database.
I have the following data in one table:
ID ID_X ID_Y
100 1 2
101 1 1
102 1 1
103 1 2
104 5 10
105 5 11
106 5 10
107 5 11
108 8 20
109 8 30
110 8 20
How to write select statement to get the following results?
ID ID_X ID_Y
100 1 2
101 1 1
104 5 10
105 5 11
108 8 20
109 8 30
I know that it is a kind of group by ID_X and ID_Y but how to select also "ID" column without grouping by it?
Maybe there is a way to select using distinct? or group by with subselect? Please help :)
You can use an aggregate function like MIN() or MAX(). In your case you want MIN() to get those specific results.
SELECT MIN(ID), ID_X, ID_Y
FROM [tablename]
GROUP BY ID_X, ID_Y
Try this using Distinct on
select *
from
(
select distinct on (id_x, id_y) ID, id_x, id_y
FROM t order by id_x, id_y,id
) q
order by id
Seems like you want a GROUP BY. Use MIN() to return each group's lowest ID:
select min(ID), ID_X, ID_Y
from tablename
group by ID_X, ID_Y
Alternatively, you can do a NOT EXISTS:
select *
from tablename t1
where not exists (select 1 from tablename t2
where t2.ID_X = t1.ID_X
and t2.ID_Y = t1.ID_Y
and t2.ID < t1.ID)
I.e. return a row as long as there are no (other) row with same ID_X and ID_Y but a lower ID.

Query to return all results except for the first record

I have a archive table that has records of transactions per locationID.
A location will have 0, 1 or many rows in this table.
I need a SELECT query that will return rows for any location that has more than 1 row, and to skip the first entry.
e.g.
Transactions table
transactionId locationId amount
1 11 2343
2 11 23434
3 25 342
4 32 234
5 77 234
6 11 38938
7 43 234
8 43 1235
So given the above, since the locationID has multiple rows, I will get back all rows except for the first one (lowest transacton_id):
2 11 23434
6 11 38938
8 43 1235
You can use row_number to do this. This assumes there would be no duplicate transactionid's.
select transactionid,locationid,amount
from
(select t.*, row_number() over(partition by locationid order by transactionid) as rn
from transactions t) t
where rn > 1
The other answer is fine. You could also write it this way, it might give you a little insight into grouping practices:
SELECT Transactions.TransactionID, Transactions.locationID, Transactions.amount
FROM Transactions INNER JOIN
(SELECT locationID, MIN(TransactionID) AS MinTransaction,
COUNT(TransactionID) AS CountTransaction
FROM Transactions
GROUP BY locationID) TableSum ON Transactions.locationID = TableSum.locationID
WHERE (Transactions.TransactionID <> TableSum.MinTransaction) AND
(TableSum.CountTransaction > 1)

Get specified row ranking number

Here is the rows looks like:
Id Gold
1 200
2 100
3 300
4 900
5 800
6 1000
What I want to achieve is getting the rank number whose Id equals to 5, which is order by Gold descending.
So after ordering, the intermediate rows should be(NOT RETURN):
Id Gold
6 1000
4 900
5 800
And the SQL should just return 3, which is the ranking of Id = 5 row.
What is the most efficient way to achieve this?
You simply want top, I think:
select top 3 t.*
from t
order by gold desc;
If you want the ranking of id = 5:
select count(*)
from t
where t.gold >= (select t2.gold from t t2 where t2.id = 5);
Try This Code By using Dense_rank():
WITH cte
AS (SELECT *,
Dense_rank()
OVER(
ORDER BY [Gold] DESC) AS rank
FROM your_table)
SELECT rank
FROM cte
WHERE id = 5