SQL: Select a partition group which dies not have a specified value - sql

I have a Table as follows
IDNumber
Name
123
Apple
123
Mango
123
Banana
126
Apple
126
Mango
126
Orange
128
Apple
128
Mango
128
Banana
151
Apple
151
Mango
151
Banana
I have used the partition by clause to partition the group based on ID Number.
with part as (Select IDNumber,count(IDNumber) over(Partition by IDNumber) cnt from myTable)
Select IDNumber from myTable where do not exists ???"Orange in partitionGroup"????
Problem:I am not really sure how this can be achieved.For a given ID, i would need a partition group to NOT select if a given value is present.
This gives me a partition where every group has three Names, But i do not want the IDNumber to be sselected if it has Orange as Name.
Please let me know in case any other details are required. Any help is highly appreciated.

You may use aggregation here:
SELECT IDNumber
FROM yourTable
GROUP BY IDNumber
HAVING COUNT(CASE WHEN Name = 'Orange' THEN 1 END) = 0;
Another way:
SELECT IDNumber
FROM yourTable
WHERE IDNumber NOT IN (
SELECT IDNumber FROM yourTable WHERE Name = 'Orange'
);

But i do not want the IDNumber to be selected if it has Orange as Name.
You can use not exists for this:
select t.*
from myTable t
where not exists (select 1
from myTable t2
where t2.id = t.id and t2.name = 'Orange'
);
You can also phrase this using window functions, but that seems unnecessary.

Related

Select data with latest date for each id

I am currently working on Microsoft Access an I am struggling to do what I want.
I have this table A:
Table A
id title name date
123 azer dfgd 1
123 afg qsd 5
123 arr poi 7
123 aur qhg 3
456 aoe aer 3
456 iuy zer 4
And I would like to get the columns id,title and name that have the latest date (highest number) for each id
With that example, the query would give
id title name date
123 arr poi 7
456 iuy zer 4
I hope you'll be able to help me.
Thanks in advance !
I would recommend a correlated subquery:
select a.*
from a
where a.date = (select max(a2.date) from a as a2 where a2.id = a.id);
For performance, you want an index on a(id, date).
With NOT EXISTS:
select t.*
from tablename t
where not exists (
select 1 from tablename
where id = t.id and date > t.date
)

I want to give serial numbers for particular IDs in existing colomns

I have Products table and 5000 records are there, and I need to update serial numbers for 2000 records.
old table
Id Name Price Recommended
45 Lotus 450 500
55 Cherry 560 500
56 Berry 789 566
new table
Id Name Price Recommended
45 Lotus 450 1
55 Cherry 560 2
56 Berry 789 3
You can't, unfortunately, use a window function directly in the set clause. You could, however, use it in a subquery, and then join that query on your table when updating:
UPDATE p
SET p.recommended = rn
FROM products p
JOIN (SELECT id, ROW_NUMBER() OVER (ORDER BY recommended) AS rn
FROM products) r ON p.id = r.id
SQLFiddle
select row_number() over ( order by (select null)), *
from OldTable
you can try with cte
;WITH cte AS
(
SELECT
id,
ROW_NUMBER() OVER (ORDER BY recommended) AS rn,
name,
price
FROM Products
)
UPDATE cte
SET recommended = rn
-- if you have any condition put here for example ( where rn <= 2000)

How can I select unique values from several columns in Oracle SQL?

Basically, I've got the following table:
ID | Amount
AA | 10
AA | 20
BB | 30
BB | 40
CC | 10
CC | 50
DD | 20
DD | 60
EE | 30
EE | 70
I need to get unique entries in each column as in following example:
ID | Amount
AA | 10
BB | 30
CC | 50
DD | 60
EE | 70
So far following snippet gives almost what I wanted, but first_value() may return some value, which isn't unique in current column:
first_value(Amount) over (partition by ID)
Distinct also isn't helpful, as it returns unique rows, not its values
EDIT:
Selection order doesn't matter
This works for me, even with the problematic combinations mentioned by Dimitri. I don't know how fast that is for larger volumes though
with ids as (
select id, row_number() over (order by id) as rn
from data
group by id
), amounts as (
select amount, row_number() over (order by amount) as rn
from data
group by amount
)
select i.id, a.amount
from ids i
join amounts a on i.rn = a.rn;
SQLFiddle currently doesn't work for me, here is my test script:
create table data (id varchar(10), amount integer);
insert into data values ('AA',10);
insert into data values ('AA',20);
insert into data values ('BB',30);
insert into data values ('BB',40);
insert into data values ('CC',10);
insert into data values ('CC',50);
insert into data values ('DD',20);
insert into data values ('DD',60);
insert into data values ('EE',30);
insert into data values ('EE',70);
Output:
id | amount
---+-------
AA | 10
BB | 20
CC | 30
DD | 40
EE | 50
I suggest using row_number() like this:
select ID ,Amount
from (
select ID ,Amount, row_number() over(partition by id order by 1) as rn
from yourtable
)
where rn = 1
However your expected results don't conform to a discrenable order, some are the first/lowest while some the last/highest so I wasn't sure what to include for the ordering.
My solution implements recursive with and makes following: first - select minival values of ID and amount, then for every next level searches values of ID and amount, which are more than already choosed (this provides uniqueness), and at the end query selects 1 row for every value of recursion level. But this is not an ultimate solution, because it is possible to find a combination of source data, where query will not work (I suppose, that such solution is impossible, at least in SQL).
with r (id, amount, lvl) as (select min(id), min(amount), 1
from t
union all
select t.id, t.amount, r.lvl + 1
from t, r
where t.id > r.id and t.amount > r.amount)
select lvl, min(id), min(amount)
from r
group by lvl
order by lvl
SQL Fiddle
I knew that there is an elegant solution! Thanks to friend of mine for a tip:
select max(ID), mAmount from (
select ID, max(Amount) mAmount from table group by ID
)
group by mAmount;
Maybe something like this can solve:
WITH tx AS
( SELECT ROWNUM ROW_NUMBER,
t.id,
t.amount
FROM test t
INNER JOIN test t2
ON t.id = t2.id
AND t.amount != t2.amount
ORDER BY t.id)
SELECT tx1.id, tx1.amount
FROM tx tx1
LEFT JOIN tx tx2
ON tx1.id = tx2.id
AND tx1.ROW_NUMBER > tx2.ROW_NUMBER
WHERE tx2.ROW_NUMBER IS NULL

Getting the most recent data from a dataset based on a date field

This seems easy, but I can't get it. Assume this dataset:
ID SID AddDate
1 123 1/1/2014
2 123 2/3/2015
3 123 1/4/2010
4 124
5 124
6 125 2/3/2012
7 126 2/2/2012
8 126 2/2/2011
9 126 2/2/2011
What I need is the most recent AddDate and the associated ID for each SID.
So, my dataset should return IDs 2, 5, 6 and 7
I tried doing a max(AddDate), but it won't give me the proper ID that's associated with it.
My SQL string:
SELECT First(Table1.ID) AS FirstOfID, Table1.SID, Max(Table1.AddDate) AS MaxOfAddDate
FROM Table1
GROUP BY Table1.SID;
You can use a subquery that returns the Maximum add date for each Sid, then you can join back this subquery to the dataset table:
SELECT
MAX(id)
FROM
ds INNER JOIN (
SELECT Sid, Max(AddDate) AS MaxAddDate
FROM ds
GROUP BY ds.Sid) mx
ON ds.Sid = mx.Sid AND (ds.AddDate=mx.MaxAddDate or MaxAddDate IS NULL)
GROUP BY
ds.Sid
the join still has to succeed if the MaxAddDate is NULL (there's no AddDate), and in case there are multiple ID that matches, it looks like you want the biggest one.
You can change your query to get the grouping first and then perform a JOIN like
SELECT First(Table1.ID) AS FirstOfID,
Table1.SID, xx.MaxOfAddDate
FROM Table1 JOIN (
SELECT ID, Max(AddDate) AS MaxOfAddDate
FROM Table1
GROUP BY SID) xx ON Table1.ID = xx.ID;
Try
select SID from table1
where addDate in (select max(addDate) from Table1)

SQL query, count and group by

If I have data like this:
Key
Name
1
Dan
2
Tom
3
Jon
4
Tom
5
Sam
6
Dan
What is the SQL query to bring back the records where Name is repeated 2 or more times?
So the result I would want is
Tom
Dan
Couldn't be simpler...
Select Name, Count(Name) As Count
From Table
Group By Name
Having Count(Name) > 1
Order By Count(Name) Desc
This could also be extended to delete duplicates:
Delete From Table
Where Key In (
Select Max(Key)
From Table
Group By Name
Having Count(Name) > 1
)
select name from table group by name having count(name) > 1
This could also be accomplished by joining the table with itself,
SELECT DISTINCT t1.name
FROM tbl t1
INNER JOIN tbl t2
ON t1.name = t2.name
WHERE t1.key != t2.key;