SQL server: Compare rows - sql

I need a query which can do the following operation.
I have a table with 2 columns
ID Values
1 1
1 2
1 3
1 4
2 2
2 5
2 6
if you see for ID 1 I have 1,2,3 and 4 as values and for ID 2 I have 2, 5 and 6.
I want to write a query which return the following
1(-)
4(-)
5(+)
6(+)
mean 1 and 4 are deleted and 5 and 6 are added by comparing the two ids.
Is it possible? Please let me know
Thanks

This will give you 1 & 4:
select a.values
from my_table a
where not exists (
select * from my_table b where b.values = a.values and b.ID = 2)
and a.ID = 1
and this will give you 5 & 6:
select a.values
from my_table a
where not exists (
select * from my_table b where b.values = a.values and b.ID = 1)
and a.ID = 2

Something like:
(
SELECT T.Value FROM dbo.Table T WHERE T.ID = 1
EXCEPT
SELECT T.Value FROM dbo.Table T WHERE T.ID = 2
)
UNION
(
SELECT T.Value FROM dbo.Table T WHERE T.ID = 2
EXCEPT
SELECT T.Value FROM dbo.Table T WHERE T.ID = 1
)
That will get you the list of values that are associated with 1 but not 2, and 2 but not 1. You could easily multiply the values from one of those subqueries by -1 to differentiate them, or run them as two separate queries.

Related

Get all the users that has only 1 category in SQL

I have this table with U that represents "User" and C that represents "Category". I need a way to query how many categories a user has and limit that to 1. So basically, I need to get all the users that has only 1 category. How can this be achieved with SQL (PostgreSQL)?
I've tried to find any solution on Stackoverflow for a while now, but without success.
id U C
1 3 5
2 1 3
3 3 5
4 5 2
5 11 5
6 11 5
Expected result:
id U C
1 1 3
2 5 2
That is easy with the HAVING clause:
SELECT max(id), u, max(c)
FROM atable
GROUP BY u HAVING count(c) = 1
You can use window functions for your purpose.
Window Functions
SELECT DISTINCT * FROM(
SELECT a.*,count(c)over(partition by id) as cnt
FROM testtable a
) WHERE CNT=1
If you have no duplicates, you can use not exists:
select t.*
from t
where not exists (select 1 from t t2 where t2.u = t.u and t2.c <> t.c);
If you have duplicates of u/c, just use id:
select t.*
from t
where not exists (select 1 from t t2 where t2.u = t.u and t2.id <> t.id);
If you want to reassign a sequential number, then use row_number() as well:
select row_number() over (order by id) as new_id, t.*
from t
where not exists (select 1 from t t2 where t2.u = t.u and t2.c <> t.c);

Select groups of users if one or more user made a comment

Consider the following table1
user group comment
----------------------
1 a foo
2 a
3 a
4 b bar
5 c
6 c
7 d
8 d
9 d
10 d
11 e bax
12 e baz
I need to make 2 queries, each of which returns groups:
query 1: groups of which at least 1 user has made a comment
query 2: groups of which no user has made a comment
result of query 1:
user group comment
----------------------
1 a foo
2 a
3 a
4 b bar
11 e bax
12 e baz
result of query 2:
user group comment
----------------------
5 c
6 c
7 d
8 d
9 d
I tried the following but then I saw some of the same users in both groups:
select *
from [table1] t1
where t1.[group] in (
select distinct [group] from [table] where [comment] <> ''
)
order by t1.[user] asc
select *
from [table1] t1
where t1.[group] in (
select distinct [group] from [table] where [comment] = ''
)
order by t1.[user] asc
I then realised this is because in the same group, comment can be either set (comment <> '') AND not set (comment = '') but I don't know how to solve this in my queries.
Any help would be greatly appreciated.
You can use exists and not exists:
This gives you the users of groups in which comments have been posted.
select t.*
from mytable t
where exists (
select 1 from mytable t1 where t1.group = t.group and t1.comment is not null
)
To get the users of groups without any comment, you can just turn exists to not exists.
You can use exists and not exists. For groups with a comment:
select t1.*
from table1 t1
where exists (select 1
from table1 tt1
where tt1.group = t1.group and
tt1.comment is not null
);
Use not exists for the other set of rows.

Filter out entire group based on item ranking in SQL

I have a table as shown below:
group item rank
1 A 1
1 B 2
1 C 3
2 A 2
2 B 1
3 A 1
3 C 2
I want those groups data only, where item A has rank 1 as shown below:
group item rank
1 A 1
1 B 2
1 C 3
3 A 1
3 C 2
In group 2, A has rank 2, therefore not a part of output.
One way is using an IN clause
select *
from yourTable
where id in (select id from yourtable where item='A' and rank = 1)
you could use a subquery for get the involved id and the join
select * from my_table m
inner join (
select distinct id
from my_table
where item = 'A'
and rank = 1
) t on t.id = m.id

Conditional delete in oracle using sql or plsql

We have an oracle table x and we have values in it in a following manner
num1 dt cd
1 24-06-2017 3
1 24-06-2017 4
2 24-06-2017 1
2 24-06-2017 2
2 24-06-2017 4
2 25-06-2017 3
2 25-06-2017 4
Now I have to delete duplicate data from this table using num1,dt , if num1 and dt have any other value instead of 4 then it should exist in table and cd=4 should be deleted. Here in below condition 3 or any other value should exist but it should not be 4
1 24-06-2017 3
1 24-06-2017 4
Now here 4 should be deleted completely for 2 , 24/06/2017 and only 1 rows of each field should exist in the table ie :2 24/06/2017 1 and 2 24/06/2017 2 should exist.
2 24-06-2017 1
2 24-06-2017 2
2 24-06-2017 4
Now in third case if we dont have any other value except 4 for num1 and date then we should have only 1 row of 4 for these dates.
3 26-06-2017 4
3 26-06-2017 4
Please provide solution for this, help will be appreciated.
Oracle conveniently has rowid, which can really help with this. Here is an approach that uses rowid:
delete from t
where t.rowid <> (select max(rowid) keep (dense_rank first
order by (case when cd = 4 then 2 else 1 end)
)
from t
where t2.num1 = t.num1 and t2.date = t.date
);
This will keep exactly one row per num1/date comparison.
If you are using Oracle 12C+, then a more intuitive form of this query is:
delete from t
where t.rowid <> (select rowid
from t
where t2.num1 = t.num1 and t2.date = t.date
order by (case when cd = 4 then 2 else 1 end)
fetch first 1 row only
);
This doesn't work in earlier versions because fetching one row requires an additional subquery -- and that subquery cannot be correlated with the outer query.
If I get your question right it might work like this:
DELETE FROM table
WHERE (num1, dt, ct) IN (SELECT num1, dt, 4
FROM table
GROUP BY num1, dt
HAVING MAX(ct) != 4
OR MIN(ct) != 4)
OR ((num, dt, ct) IN (SELECT num1, dt, MAX(ct)
FROM table
GROUP BY num1, dt
HAVING MAX(ct) = 4
AND MIN(ct) = 4)
AND rowid NOT IN (SELECT MIN(rowid)
FROM table
GROUP BY num1, dt
HAVING MAX(ct) = 4
AND MIN(ct) = 4))

Select Grouped Column Values Where Have Same Id In SQL Server

I have a table like this.
TABLE-1
id Code
-----------------
1 N188
1 N1Z2
1 N222
2 N189
2 N1Z2
2 N1Z3
3 N188
3 A123
3 B321
4 N188
4 A333
4 B444
I want to select id and code only code has N188.Result should like this:
TABLE-2
id Code
---------------
1 N188
1 N1Z2
1 N222
3 N188
3 A123
3 B321
4 N188
4 A333
4 B444
How can I write sql for this in SQL Server?
Thanks
You can use EXISTS for this:
SELECT id, code
FROM table1 t
WHERE EXISTS (
SELECT 1
FROM table1 t2
WHERE t.id = t2.id
AND t2.Code = 'N188'
)
Condensed SQL Fiddle Demo
Using INNER JOIN
SELECT *
FROM tablename A
JOIN (SELECT id
FROM tablename
WHERE code = 'N188') B
ON a.id = b.id
Here is an alternative method that uses window functions:
select id, code
from (select t.*,
sum(case when code = 'N188' then 1 else 0 end) over (partition by id) as cnt_n188
from table t
) t
where cnt_n188 > 0;