Get a limited number of rows for each match in Postgres - sql

I have a table like:
1 a a1 a2
1 b b1 b2
1 c c1 c2
1 d d1 d2
2 a a1 a2
2 b b1 b2
2 c c1 c2
3........
3........
........
........
n x x1 x2
n y y1 y2
n z z1 z2
From this, I want to get for each number(1,2,3,4....n) some specified number(say 2) of rows.
Result:
1 a a1 a2
1 b b1 b2
2 a a1 a2
2 b b1 b2
.........
.........
n x x1 x2
n y y1 y2
I am trying to do group by and string_agg(). But I can't limit it to a specified number.
How can I go about it?

This can be done using a window function:
select nr, col1, col2, col3
from (
select nr, col1, col2, col3,
row_number() over (partition by nr order by col1) as rn
from the_table
) t
where rn <= 2;
If you want to influence which rows are returned, you can adjust the order by that defines the ordering of the rows in the window function.

Related

Looking for an alternative SQL statement

Given the following table with 2 columns:
c1 c2
------------
a1 | b1
a1 | b1
a2 | b2
a2 | b3
a3 | b3
I want to return those values from column c2 where the value of c2 column appears multiple times for the same c1 value. I am doing the following SQL query to return the required result:
SELECT DISTINCT ( c2 ) AS c
FROM ( SELECT c1 , c2 , COUNT (*) AS rowcount
FROM table
GROUP BY c1 , c2 HAVING rowcount > 1 )
Result:
c
---
b1
Is there any alternative SQL statement of the above query?
Based on your description, you can use:
select distinct c1
from (select t.*, count(*) over (partition by c2) as cnt
from t
) t
where cnt >= 2;
Based on your sample results:
select c1
from t
group by c1
having count(*) >= 2;
And based on the revised question:
select c2
from t
group by c2
having count(*) >= 2;
Use count in having clause instead of using subquery:-
select c1
from table
group by c1
having count(c2) > 1
Most answers above will work if you want all the values in c1 that appear more than once in the table (even with the same value on c2).
If you want to measure only values of c1 that may have multiple DISTINCT values on c2 you can use:
SELECT c1
FROM table
GROUP BY c1
HAVING COUNT(DISTINCT c2) > 1

Postgresql function for passing arguments dynamically

I created a function abcd_insert() which inserts data into table abcd, which has 8 columns. The code inside the function looks similar to below :
BEGIN
INSERT INTO abcd
VALUES
(
x ,
y ,
select sum(count) from (select count(*) from a where a1 = x and a2 = y and a3 = 1 union all select count(*) from b where b1 = x and b2 = y and b3 = 1 ) as n1,
select sum(count) from (select count(*) from a where a1 = x and a2 = y and a2 = 2 union all select count(*) from b where b1 = x and b2 = y and b3 = 2 ) as n2 ,
select sum(count) from (select count(*) from a where a1 = x and a2 = y and a2 = 3 union all select count(*) from b where b1 = x and b2 = y and b3 = 3 ) as n3 ,
select sum(count) from (select count(*) from a where a1 = x and a2 = y and a2 = 4 union all select count(*) from b where b1 = x and b2 = y and b3 = 4 ) as n4 ,
select sum(count) from (select count(*) from a where a1 = x and a2 = y and a2 = 5 union all select count(*) from b where b1 = x and b2 = y and b3 = 5 ) as n5 ,
SELECT sum(q1) from
(SELECT CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END as q1 FROM p1 where p11 = x and p12 = y union all
SELECT CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END as q1 FROM p2 where p21 = x and p22 = y ) as q1
);
END;
'x' and 'y' are my input parameters whose values will be passed to the function abcd_insert(). a,b,p1 and p2 are tables within the same schema.
When I pass 'x' and 'y' to the function at run time, it throws error.
Can someone please tell me what I am doing wrong here.
I think you'd better to specify the column names in your insert statement.
Insert into abcd ("column1",...,"column8") values ...
And please post the error, so that others can help.
with your sample code, you need brackets around queries, so you would use their result, eg:
t=# create table t (i int, e int);
CREATE TABLE
t=# create or replace function f(x int) returns void as $$
begin
insert into t values (x, (select 1 where x > 0));
end;
$$ language plpgsql;
CREATE FUNCTION
t=# select f(1);
f
---
(1 row)
t=# select * from t;
i | e
---+---
1 | 1
(1 row)

How to remove the null values and get the table like shown below?

Here i have the oracle db table like shown below:
name a1 b1 c1
---- --- --- ---
a z null null
a null y null
a nul null z
b y z null
b null null m
So my expected output table is:
name a1 b1 c1
---- --- --- ---
a z y z
b y z m
As aggregate functions [except count(*)] ignores NULL, you can simply use MAX or MIN and group by to get your desired result as below.
SELECT name,
max(a1) AS a1,
max(b1) AS b1,
max(c1) AS c1
FROM table1
GROUP BY name
OR
SELECT name,
min(a1) AS a1,
min(b1) AS b1,
min(c1) AS c1
FROM table1
GROUP BY name
Result:
NAME A1 B1 C1
------------------
a z y z
b y z m
DEMO
you can try something like that:
select name, max(nvl(a1, '')), max(nvl(a2, '')), max(nvl(a3, ''))
from table
group by name
SELECT name, MAX( a1 ) , MAX( b1 ) , MAX( c1 )
FROM Table
GROUP BY name
For SQL this will work

sql query to find what is not in group three

i have one SQL Table in which some dummy data.
i want that dummy data and update that row with column type
my table tbl
ID D1 M1 C1 QTY TYPE
1 D1 M1 C1 1 Y
2 D1 M2 C1 2 Y
3 D1 M3 C1 3 Y
4 D1 M1 C1 1 Y
5 D2 M1 C1 1 Y
6 D2 M2 C1 2 Y
7 D2 M3 C2 3 Y
8 D2 M1 C1 1 Y
9 D2 M2 C1 2 Y
10 D3 M1 C1 1 Y
11 D3 M2 C1 2 Y
12 D3 M3 C1 3 Y
13 D3 M1 C1 1 Y
14 D3 M2 C1 2 Y
15 D3 M3 C1 3 Y
16 D3 M1 C2 1 Y
grouping on Column D1 and M1
I have a N no. of records, now I have to identify group of 3 record and if any record remain then it should be set as "No" else "yes"
Ex:
condition 1: If I have 4 records, then make a group of 3-3 records so remain last 1 record should be set an "no".
condition 2: If I have 5 records, then make a group of 3-3 records so remain last 2 records will be set as "No"
Condition 3: if I have 7 records, then make a group of 3-3 records so remain last 1 record will be set as "No"
my expected answer is as below
ID D1 M1 C1 QTY TYPE
1 D1 M1 C1 1 YES
2 D1 M2 C1 2 YES
3 D1 M3 C1 3 YES
4 D1 M1 C1 1 NO
5 D2 M1 C1 1 YES
6 D2 M2 C1 2 YES
7 D2 M3 C2 3 YES
8 D2 M1 C1 1 NO
9 D2 M2 C1 2 NO
10 D3 M1 C1 1 YES
11 D3 M2 C1 2 YES
12 D3 M3 C1 3 YES
13 D3 M1 C1 1 YES
14 D3 M2 C1 2 YES
15 D3 M3 C1 3 YES
16 D3 M1 C2 1 NO
SQLFIDDLE
Please tell me solution.
Is this what you are looking for?
with toupdate as (
select t.*, row_number() over (partition by d1 order by id) as seqnum,
count(*) over (partition by d1) as cnt
from tbl t
)
update toupdate
set type = (case when seqnum <= 3*(cnt /3) then 'yes' else 'no' end);
You can also run similar logic as a select:
select t.*, (case when seqnum <= 3*(cnt /3) then 'yes' else 'no' end)
from (
select t.*, row_number() over (partition by d1 order by id) as seqnum,
count(*) over (partition by d1) as cnt
from tbl t
) t;

Generate Report from SQL Query

I have the following table:
Code State Site Date
----------
x1 2 s1 d1
x2 2 s1 d2
x3 2 s1 d3
x1 3 s2 d4
x2 3 s1 d5
x3 3 s1 d6
x4 2 s2 d7
x5 2 s2 d8
x3 2 s1 d9
----------
(here d1<d2....<d7)
My goal is to make a report (new table) for every Code:
For those Code that been in state 2 and 3 while Date in state 2 less than date in state 3 and the Code in the same site
For the above table the result would be:
Code Date
----------
x2 d2
x3 d3
x2 d5
x3 d6
----------
What I tried was:
Select Code,Date,Site from Transactions where State='2' and Site in
(Select Site from Transactions where State='3')
But this query isn't enough because for the given table it returns:
Code Date
----------
x2 d2
x3 d3
x2 d5
x3 d6
x3 d9
----------
Which isn't what I want excatly since here in date d9 hasnt a pairing with state 3 so that d9< of that pair...
Hope all this make sense.
If they do is there a SQL query to achieve my purpose?
For the set you gave this should work, although since you did not mention what happens in terms of multiple dates for a specific state I did not answer that question.
Long but it works
declare #WhatEverYourTableNameIs Table
(
Code varchar(2),
State int,
Site VarChar(2),
DateGotten Date
)
Insert into #WhatEverYourTableNameIs
Values
('x1',2,'s1','2014-1-1'),
('x2',2,'s1','2014-1-2'),
('x3',2,'s1','2014-1-3'),
('x1',3,'s2','2014-1-4'),
('x2',3,'s1','2014-1-5'),
('x3',3,'s1','2014-1-6'),
('x4',2,'s2','2014-1-7'),
('x5',2,'s2','2014-1-8'),
('x3',2,'s1','2014-1-9')
SELECT * into #MyTemp
FROM
(
SELECT Code, [State],Site [Site],DateGotten
FROM #WhatEverYourTableNameIs
GROUP BY Code, [State], Site, DateGotten
) a
SELECT *
FROM
(
SELECT DISTINCT a.Code, a.State, a.Site, a.DateGotten
FROM #MyTemp a
JOIN (
SELECT *
FROM #MyTemp
WHERE [State] =3
) b ON a.Code = b.Code and a.Site = b.Site
WHERE a.[State] = 2 and a.DateGotten < b.DateGotten
UNION
SELECT DISTINCT b.Code, b.State, b.Site, b.DateGotten
FROM #MyTemp a
JOIN (
SELECT *
FROM #MyTemp
WHERE [State] =3
) b on a.Code = b.Code and a.Site = b.Site
WHERE b.[State] = 3 and a.DateGotten < b.DateGotten
) a
order by a.DateGotten
drop table #MyTemp
>Code State Site DateGotten
>x2 2 s1 2014-01-02
>x3 2 s1 2014-01-03
>x2 3 s1 2014-01-05
>x3 3 s1 2014-01-06