How to select group by 2 columns + id? - sql

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.

Related

Postgres query with limit that selects all records with similar identifier

I have a table that looks something like this:
customer_id
data
1
123
1
456
2
789
2
101
2
121
2
123
3
123
4
456
What I would like to do is perform a SELECT combined with a LIMIT X to get X number of records as well as any other records that have the same customer_id
Example query: SELECT customer_id, data FROM table ORDER BY customer_id LIMIT 3;
This query returns:
customer_id
data
1
123
1
456
2
789
I'd like a query that will look at the last customer_id value and return all remaining records that match beyond the LIMIT specified. Is it possible to do this in a single operation?
Desired output:
customer_id
data
1
123
1
456
2
789
2
101
2
121
2
123
In Postgres 13 can use with ties:
select t.*
from t
order by customer_id
fetch first 3 rows with ties;
In earlier versions you can use in:
select t.*
from t
where t.customer_id in (select t2.customer_id
from t t2
order by t2.customer_id
limit 3
);
You can use corelated subquery with count as follows:
Select t.*
From t
Where 3 >= (select count(distinct customer_id)
From t tt
where t.customer_id >= tt.customer_id)

group by in SQL, improvement of query

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

How to grouping with distinct or group max sql

i have date like this Data
id name period difference
6172 A 6 10
6172 A 3 10
10099 AB 12 24
10099 AB 6 24
10099 AB 3 24
10052 ABC 12 26
10052 ABC 6 26
10052 ABC 3 26
9014 ABCD 12 21
9014 ABCD 6 21
9014 ABCD 3 21
how to get result like this
id name period difference
6172 A 6 10
10099 AB 12 24
10052 ABC 12 26
9014 ABCD 12 4
i try with distinct on (id), but the result like this
id name period difference
6172 A 6 10
10099 AB 6 24
10052 ABC 6 26
9014 ABCD 6 4
The query you want looks something like:
SELECT DISTINCT ON (id) *
FROM Data
ORDER BY id, period DESC;
Demo
This is probably the most efficient way to write your query on Postgres. Note that DISTINCT ON syntax does not support more than one column in the ON clause. The above logic happens to work here assuming that id would uniquely identify each group (that is, that id would always be unique). If not, then we might have to resort to using ROW_NUMBER with a partition over id and name.
using max()
select id, name, t2.period, difference from tableA t1
inner join
(select id, max(period) as period from tableA
group by id) t2 on t2.id = t1.id
using distinct()
select distinct id, name, t2.period, difference from tableA
it seems you need just max()
select id,name,max(period),max(difference)
from table group by id,name
Though i have not found difference=4 in your sample data but you used that on output,so i guessed its your typo
Use max()
select id, name, max(period), difference from tablename
group by id, name,difference
You can try my code:
SELECT
id, name, max(period), difference
FROM
data_table
group by id, name,difference
order by name
This is a demo link http://sqlfiddle.com/#!17/9ab8d/2

SELECT clause with SUM condition

Have this table :
//TEST
NUMBER TOTAL
----------------------------
1 158
2 355
3 455
//TEST1
NUMBER QUANTITY UNITPRICE
--------------------------------------------
1 3 5
1 3 6
1 3 4
2 4 8
3 5 4
I used following query:
SELECT t.NUMBER,sum(t.TOTAL),NVL(SUM(t2.quantity*t2.unitprice),0)
FROM test t INNER JOIN test1 t2 ON t.NUMBER=t2.NUMBER
GROUP BY t.NUMBER;
OUTPUT:
NUMBER SUM(TOTAL) SUM(t2.quantity*t2.unitprice)
-----------------------------------------------------------
1 474 45 <--- only this wrong
2 355 32
It seem like loop for three times so 158*3 in the record.
EXPECTED OUTPUT:
NUMBER SUM(TOTAL) SUM(t2.quantity*t2.unitprice)
-----------------------------------------------------------
1 158 45
2 355 32
You have to understand that the result of your join is something like this:
//TEST1
NUMBER QUANTITY UNITPRICE TOTAL
--------------------------------------------------------------
1 3 5 158
1 3 6 158
1 3 4 158
2 4 8 355
3 5 4 455
It means you don't need to apply a SUM on TOTAL
SELECT t.NUMBER,t.TOTAL,NVL(SUM(t2.quantity*t2.unitprice),0)
FROM test t INNER JOIN test1 t2 ON t.NUMBER=t2.NUMBER
GROUP BY t.NUMBER, t.TOTAL;
Something like this should work using a subquery separating the sums:
select t.num,
sum(t.total),
test1sum
from test t
join (
select num, sum(qty*unitprice) test1sum
from test1
group by num
) t2 on t.num = t2.num
group by t.num, test1sum
SQL Fiddle Demo
In regards to your sample data, you may not even need the additional group by on the test total field. If that table only contains distinct ids, then this would work the same:
select t.num,
t.total,
sum(qty*unitprice)
from test t
join test1 t2 on t.num = t2.num
group by t.num, t.total

how to write query for split two rows?

I have order table
OrderId OrderStatusId CurrencyId PromotionCode
------------------------------------------------------
137 5 1 123a-123d
138 5 1 123a-123d-234c
I want to split the PromotionCode column like this:
Result:
OrderId OrderStatusId CurrencyId PromotionCode
-----------------------------------------------------
137 5 1 123a
137 5 1 123d
138 5 1 123a
138 5 1 123d
138 5 1 234c
Please help me ...
is it possible to do... any way plz help me asas possible....
If the promotion codes are always 4 characters long, the simplest way is probably a union:
select id, substring(code,1,4)
from YourTable
where LEN(code) >= 4
union all
select id, substring(code,6,4)
from YourTable
where LEN(code) >= 9
union all
select id, substring(code,11,4)
from YourTable
where LEN(code) >= 14
<etc>
For a more flexible solution, have a look at one of the various Split functions. After creating this function in your database, you can invoke it like:
select t.id, s.items
from YourTable t
cross apply
dbo.Split(t.code,'-') s
Both queries will produce the result as requested in your answer.