PostgreSQL : Display the id which do not have a specific attribute value - postgresql

Structure
ID
Attribute_id
Value
id_1
attribute_1
name_1
id_1
attribute_1
name_2
id_1
attribute_1
name_3
id_1
attribute_2
xxx
id_1
attribute_3
yyy
id_2
attribute_2
aaa
id_2
attribute_3
bbb
id_3
attribute_1
name_1
id_3
attribute_2
xxx
Result expected
ID
id_2
Need to display ids not having name (not associated with attribute_1)
How to achieve this?

One option is to use EXCEPT operator:
select id from your_table
except
select id from your_table where attribute_id = 'attribute_1';

Related

Postgres | Get all ids after a promo code is used

I'm trying to get all order ids that used a specific promo code (ABC123). However, I want to see all subsequent orders, rather than just all the ids. For example, if we have the following table:
Account_id
order_id
promo_code
1
123
NULL (no promo code used)
2
124
ABC123
3
125
HelloWorld!
2
125
NULL
1
126
ABC123
2
127
HelloWorld!
3
128
ABC123
Ideally, what I want to get is this (ordered by account_id):
Account_id
order_id
promo_code
1
126
ABC123
2
124
ABC123
2
125
NULL
2
127
HelloWorld!
3
128
ABC123
As you can see promo_code = ABC123 is like a placeholder in which all once that ID is found, I want all preceding order_ids.
So far to filer all the account_ids that used this promo_code is:
SELECT account_ids, order_id, promo_code
FROM orders
WHERE account_id IN (SELECT account_id FROM order WHERE promo_code = 'ABC123');
This allows me to get the account_ids that have an order where the desired promo_code was used.
Thanks in advance!
Extract all account_id-s that used 'ABC123' and the smallest corresponding order_number-s (the t CTE) then join these with the table and filter/order the result set.
with t as
(
select distinct on (account_id) account_id, order_id
from the_table where promo_code = 'ABC123'
order by account_id, order_id
)
select the_table.*
from the_table
inner join t on the_table.account_id = t.account_id
where the_table.order_id >= t.order_id -- the subsequent orders
order by the_table.account_id, the_table.order_id;
SQL Fiddle

Need to combine the sales of 2 records with different ID's and the record with highest sale id should be in the result set

I have a table with records belonging to same person but the person was assigned with 2 different id's.
I need to combine the sales and then hold on to the id having highest sales.
For Example:
ID Name Sales
1 ABC 10
4 ABC 60
5 xyz 100
6 xyz 10
I need result as
ID Name Sales
4 ABC 70
5 XYZ 110
Please help me with a sql query for the above.
try this:
create table #mytable
(id int,
name nvarchar(20),
Sales int)
insert into #mytable
values
(1,'ABC',10),
(4,'ABC',60),
(5,'xyz',100),
(6,'xyz',10)
select (select top(1) ID
from #mytable r2
where r2.name=r1.name
and r2.Sales=MAX(r1.Sales))as ID,
name,
sum(Sales)
from #mytable r1
group by name
drop table #mytable

How do I select the min opendate from a list of duplicates?

I have 3 columns. SSN|AccountNumber|OpenDate
1 SSN may have multiple AccountNumbers
Each AccountNumber has a corresponding OpenDate
In my list I have many SSN's, each containing several account numbers which may have been opened on different days.
I want the results of my query to be SSN|earlest OpenDate|AccountNumber that corresponds with the earliest opendate.
I'm dealing with about 200,000 records.
EDIT: First I did
select SSN, min(OpenDate), AcctNumber from Table Group By SSN, AccountNumber
but that didn't quite give me the correct data.
The raw data gives me something like this:
SSN | AcctNumber | OpenDate
---------------------------
10 101 Jan
10 102 Feb
10 103 Mar
Where I got 10, Jan, and AccNumber 102 which is not the account number that is associated with Jan OpenDate After looking at others, I found that the account number I got was just one of the account numbers associated with that SSN rather than the one that corresponds with the min(OpenDate)
WITH CTE AS ( SELECT SSN, AcctNumber, OpenDate, ROW_NUM() OVER (PARTITION BY SSN ORDER BY OpenDate DESC) AS RN ) SELECT SSN, AcctNumber, OpenDate FROM CTE WHERE RN=1;
If your table is like this:
SSN | AcctNumber | OpenDate
---------------------------
10 101 April
10 101 May
10 102 April
20 201 June
20 201 July
Do you want your query to return this?
SSN | AcctNumber | OpenDate
---------------------------
10 101 April
10 102 April
20 201 June
Then you would use this query:
select ssn, min(OpenDate), acctNumber from tbl group by ssn, acctNumber
You can try this..
select SSN , AcctNumber, OpenDate
from (SELECT SSN , AcctNumber, OpenDate
, ROW_NUMBER() OVER ( PARTITION BY SSN, ORDER BY OpenDate ASC ) AS RN
FROM table) AS temp
WHERE temp.RN= 1

PostgreSQL - JOIN on string_agg

I have three tables.
Students
student_id | name
1 Rhon
Subjects
subject_id | subject_name | student_id
1 Physics 1
2 Math 1
Grades
grade_id | student_id | subject_id | grade
1 1 1 90
2 1 2 89
3 1 2 88
I would like to result to be like this:
student_id | student_name | subject_name | grades
1 Rhon Physics 90
1 Rhon Math 88,89
My current query is:
SELECT students.student_id, subjects.subject_id, string_agg(grades.grade, ',')
FROM students
JOIN subjects ON students.student_id = subjects.student_id
JOIN grades ON subjects.subject_id = grades.subject_id;
Is there something wrong with my query? Am I missing something? The error says that student_id needs to be in a GROUP BY clause but I don't want that.
You can do this with a sub-query:
SELECT s.student_id, s.student_name, j.subject_name, g.grades
FROM students s
JOIN subjects j
JOIN (
SELECT student_id, subject_id, string_agg(grade, ',') AS grades
FROM grades
GROUP BY student_id, subject_id) g USING (student_id, subject_id);
Why exactly do you not want to GROUP BY student_id?

Delete rows where data has less sum

I have the following table:
data_id sum_value
xxx 30
xxx 40
ccc 50
aaa 60
ccc 70
aaa 80
ddd 100
eee 200
How would I delete the row where data_id = data_id and sum < sum ? Delete rows if data_id = data_id and sum_value is less and if data_id != data_id then show actual values
Expected Output
data_id sum_value
xxx 40
ccc 70
aaa 80
ddd 100
eee 200
thank you
delete from foo
using(select min(sum_value) sum
,data_id
from foo
group by data_id
having count(data_id)>1
)t
where foo.sum_value=t.sum
and foo.data_id=t.data_id
SqlFiddle - Demo
Try this:
delete from table where data_id=data_id and (SELECT min(the_field) FROM the_table)
select * from table
Assuming you only want to keep the records with the largest sum (for every id), and delete the rest:
-- the data
CREATE TABLE ztable
( data_id CHAR(3) NOT NULL
, sum_value INTEGER NOT NULL
);
INSERT INTO ztable(data_id, sum_value) VALUES
('xxx',30)
,('xxx',40)
,('ccc',50)
,('aaa',60)
,('ccc',70)
,('aaa',80)
;
-- Delete the non-largest per id:
DELETE FROM ztable del -- del cannot be the record with the largest sum
WHERE EXISTS ( -- if a record exists
SELECT * FROM ztable x
WHERE x.data_id = del.data_id -- with the same ID
AND x.sum_value > del.sum_value -- ... but a larger sum
);
Result:
CREATE TABLE
INSERT 0 6
DELETE 3
data_id | sum_value
---------+-----------
xxx | 40
ccc | 70
aaa | 80
(3 rows)
You can do this :
with src as (
SELECT DISTINCT on (data_id)
data_id as di, sum_value as sv
FROM Table1
ORDER BY data_id, sum_value DESC
)
DELETE FROM table1
WHERE ( data_id, sum_value) NOT IN (SELECT di, sv FROM src);
to only leave the row with the highest sum_value , or this:
with src as (
SELECT DISTINCT on (data_id)
data_id as di, sum_value as sv
FROM Table1
ORDER BY data_id, sum_value ASC
)
DELETE FROM table1
WHERE ( data_id, sum_value) IN (SELECT di, sv FROM src);
to only remove row with the smallest sum_value.
For supplied data both will do the same. You didn't mention what do you expect where more than two rows exist with the same data_id.
see the fiddle: http://sqlfiddle.com/#!15/92a04/1
Hmm sqlFiddle behaves strange when you modify data in the right pane. When running them both it shows that 0 rows were affected by first one, but then the second on shows only 3 rows left . However, if you run them separately - it shows that no rows were deleted, and all rows still exist. I guess it returns the table for the state defined in the left pane after each run... It has to run it in a transaction. When I added COMMIT; statement it threw an error:
Explicit commits are not allowed within the query panel.
Try theese queries on your local DB - this is how it looks on my local postgres 9.4:
testdb=# select * from Table1;
data_id | sum_value
---------+-----------
xxx | 30
xxx | 40
ccc | 50
aaa | 60
ccc | 70
aaa | 80
(6 wierszy)
testdb=# with src as (
testdb(# SELECT DISTINCT on (data_id)
testdb(# data_id as di, sum_value as sv
testdb(# FROM Table1
testdb(# ORDER BY data_id, sum_value ASC
testdb(# )
testdb-# DELETE FROM table1
testdb-# WHERE ( data_id, sum_value) IN (SELECT di, sv FROM src);
DELETE 3
testdb=# select * from Table1;
data_id | sum_value
---------+-----------
xxx | 40
ccc | 70
aaa | 80
(3 wiersze)
re-init table
testdb=# select * from Table1;
data_id | sum_value
---------+-----------
xxx | 30
xxx | 40
ccc | 50
aaa | 60
ccc | 70
aaa | 80
ddd | 100
eee | 200
(8 wierszy)
testdb=# with src as (
testdb(# SELECT DISTINCT on (data_id)
testdb(# data_id as di, sum_value as sv
testdb(# FROM Table1
testdb(# ORDER BY data_id, sum_value DESC
testdb(# )
testdb-# DELETE FROM table1
testdb-# WHERE ( data_id, sum_value) NOT IN (SELECT di, sv FROM src);
DELETE 3
testdb=# SELECT * from table1;
data_id | sum_value
---------+-----------
xxx | 40
ccc | 70
aaa | 80
ddd | 100
eee | 200
(5 wierszy)
works for me...
delete from table
USING table, table as vtable
WHERE (NOT table.ID=vtable.ID)
OR sum=(SELECT min(sum) FROM table)
try this