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
Related
I have a table like this:
ID Seq Prod
-----------------
1 001 1
2 002 1
3 001 2
4 002 2
5 003 2
I want to make a query that only gets the last "Seq" of each product, so the expected output will be something like this:
ID Seq Prod
-----------------
2 002 1
5 003 2
Any help?
A simple way is a correlated subquery:
select t.*
from t
where t.seq = (select max(t2.seq) from t t2 where t2.prod = t.prod);
For performance, you want an index on (prod, seq).
The above often has the best performance. But another way to write the query is to use window fucntions:
select t.*
from (select t.*, row_number() over (partition by prod order by seq desc) as seqnum
from t
) t
where seqnum = 1;
Yet another option is using WITH TIES
Select top 1 * with ties
From YourTable
Order By row_number() over (partition by prod order by seq desc)
Full Disclosure:
Gordon's answer is a nudge more performant (+1), but WITH TIES does not generate an extra column.
You could use a sub-query that finds the maximum ID by Prod. In the following example, replace 'myTable' with your table name:
SELECT t.*
FROM myTable t
INNER JOIN (
SELECT MAX(ID) AS ID,
Prod
FROM myTable
GROUP BY Prod
) a ON a.ID = t.ID
Output:
ID Seq Prod
2 002 1
5 003 2
Here is a quick, working fiddle.
You can write a correlated subquery as:
select T.ID,T.Seq,T.Prod
from #T1 T
where T.ID = (select max(T_Inner.ID)
from #T1 T_Inner
where T_Inner.Prod = T.Prod
group by T_Inner.Prod
)
I have a table as
Person| Count
A | 10
B | 20
C | 30
I use code as below to get above table:
select new_table.person, count(new_table.person)
from (person_table_1
inner join person_table_2
on person_table_1.user_name = person_table_2.user_all_name) new_table
group by new_table.person
However, I wish to have the percentage for each row based on overall sum in count.
Expected:
Person| Count | Percentage
A | 10 | 0.167
B | 20 | 0.333
C | 30 | 0.500
I wish it to be in 3 decimal places. Can anyome please help me. thank you.
Just do an inner query in SELECT clause
select p1.person, count(p1.person), count(p1.person) / (SELECT COUNT(p2.person) FROM person_table p2)
from person_table p1
group by p1.person
Edit: if you want only up to 3 decimal:
select
p1.person,
count(p1.person),
ROUND(count(p1.person) / (SELECT COUNT(p2.person) FROM person_table p2), 3)
from person_table p1
group by p1.person
Edit 2: OP edited his/her table
select
new_table.person,
count(new_table.person),
ROUND(
count(new_table.person) /
SELECT COUNT(new_table_COUNTER.person) FROM (
person_table_1
inner join person_table_2
on person_table_1.user_name = person_table_2.user_all_name
) new_table_COUNTER )
from
(
person_table_1
inner join person_table_2
on person_table_1.user_name = person_table_2.user_all_name
) new_table
group by new_table.person
Try below query:
declare #tbl table ([person] varchar(5));
insert into #tbl values
('a'),('a'),('a'),('b'),('b'),('c');
-- here we tabke max(rowsCnt), but we wwant any value, because every value is the same in that column
select person, count(*) * 1.0 / max(rowsCnt) [percentage] from (
select person,
count(*) over (partition by (select null)) rowsCnt
from #tbl
) a group by person
I have a table such as this:
PalmId | UserId | CreatedDate
1 | 1 | 2018-03-08 14:18:27.077
1 | 2 | 2018-03-08 14:18:27.077
1 | 3 | 2018-03-08 14:18:27.077
1 | 1 | 2018-03-08 14:18:27.077
I wish to know how many dates were created for Palm 1 and I also wish to know how many users have created those dates for Palm 1. So the outcome for first is 4 and outcome for second is 3
I am wondering if I can do that in a single query as oppose to having to do a subquery and a join on itself as in example below.
SELECT MT.[PalmId], COUNT(*) AS TotalDates, T1.[TotalUsers]
FROM [MyTable] MT
LEFT OUTER JOIN (
SELECT MT2.[PalmId], COUNT(*) AS TotalUsers
FROM [MyTable] MT2
GROUP BY MT2.[UserId]
) T1 ON T1.[PalmId] = MT.[PalmId]
GROUP BY MT.[PalmId], T1.[TotalUsers]
According to first table you could do something like this:
select count(distinct uerid) as N_Users,
count(created_date) as created_date, -- if you use count(*) you consider also rows with 'NULL'
palmid
from your_table
group by palmid
If you want "4" and "3", then I think you want:
SELECT MT.PalmId, COUNT(*) AS NumRows, COUNT(DISTINCT mt.UserId) as NumUsers
FROM MyTable MT
GROUP BY MT.PalmId
The table I have now looks something like this. Each row has a time value (on which the table is sorted in ascending order), and two values which can be replicated across rows:
Key TimeCall R_ID S_ID
-------------------------------------------
1 100 40 A
2 101 50 B
3 102 40 C
4 103 50 D
5 104 60 A
6 105 40 B
I would like to return something like this, wherein for each row, a JOIN is applied such that the S_ID and Time_Call of the next row that shares that row's R_ID is displayed (or is NULL if that row is the last instance of a given R_ID). Example:
Key TimeCall R_ID S_ID NextTimeCall NextS_ID
----------------------------------------------------------------------
1 100 40 A 102 C
2 101 50 B 103 D
3 102 40 C 105 B
4 103 50 D NULL NULL
5 104 60 A NULL NULL
6 105 40 B NULL NULL
Any advice on how to do this would be much appreciated. Right now I'm joining the table on itself and staggering the key on which I'm joining, but I know this won't work for the instance that I've outlined above:
SELECT TOP 10 Table.*, Table2.TimeCall AS NextTimeCall, Table2.S_ID AS NextS_ID
FROM tempdb..#Table AS Table
INNER JOIN tempdb..#Table AS Table2
ON Table.TimeCall + 1 = Table2.TimeCall
So if anyone could show me how to do this such that it can call rows that aren't just consecutive, much obliged!
Use LEAD() function:
SELECT *
, LEAD(TimeCall) OVER (PARTITiON BY R_ID ORDER BY [Key]) AS NextTimeCall
, LEAD(S_ID) OVER (PARTITiON BY R_ID ORDER BY [Key]) AS NextS_ID
FROM Table2
ORDER BY [Key]
SQLFiddle DEMO
This is only test example I had close by ... but i think it could help you out, just adapt it to your case, it uses Lag and Lead ... and it's for SQL Server
if object_id('tempdb..#Test') IS NOT NULL drop table #Test
create table #Test (id int, value int)
insert into #Test (id, value)
values
(1, 1),
(1, 2),
(1, 3)
select id,
value,
lag(value, 1, 0) over (order by id) as [PreviusValue],
lead(Value, 1, 0) over (order by id) as [NextValue]
from #Test
Results are
id value PreviusValue NextValue
1 1 0 2
1 2 1 3
1 3 2 0
Use an OUTER APPLY to select the top 1 value that has the same R_ID as the first Query and has a higher Key field
Just change the TableName to the actual name of your table in both parts of the query
SELECT a.*, b.TimeCall as NextTimeCall, b.S_ID as NextS_ID FROM
(
SELECT * FROM TableName as a
) as a
OUTER APPLY
(
SELECT TOP 1 FROM TableName as b
WHERE a.R_ID = b.R_ID
AND a.Key > B.Key
ORDER BY Key ASC
) as b
Hope this helps! :)
For older versions, here is one trick using Outer Apply
SELECT a.*,
nexttimecall,
nexts_id
FROM table1 a
OUTER apply (SELECT TOP 1 timecall,s_id
FROM table1 b
WHERE a.r_id = b.r_id
AND a.[key] < b.[key]
ORDER BY [key] ASC) oa (nexttimecall, nexts_id)
LIVE DEMO
Note : It is better to avoid reserved keywords(Key) as column/table names.
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)