Selecting top 3 distinct records from a table - sql-server

I have a table like this which contains the columns: country, woeid, sometopic, LastDateTime
Venezuela 23424982 Metoo
Venezuela 23424982 Chaderton
India 25424282 BossAgain
World 1 EL AVIADOR
Venezuela 23424982 ChicagoBurning and so on...
I want distinct country to be selected and only top 3 rows.
I want the result to be like this ordered by LastDateTime.
Venezuela 23424982 Chaderton
India 25424282 BossAgain
World 1 EL AVIADOR
I tried like this:
select distinct(country), woeid, sometopic, * from PopularTrends order by LastModifiedTime desc
but this didnot work.
any pointers?

Try this,
SELECT COUNTRY,WOEID,SOMETOPIC,LASTDATETIME
FROM (SELECT *,
Row_number()
OVER(
PARTITION BY COUNTRY
ORDER BY LASTDATETIME) AS RN
FROM #Yourtable)A
WHERE RN = 1

Related

unique chat records sql

I have DB which having 5 column as follows:
message_id
user_id_send
user_id_rec
message_date
message_details
Looking for a SQL Serve Query, I want to Filter Results from two columns (user_id_send,user_id_rec)for Given User ID based on following constrains:
Get the Latest Record (filtered on date or message_id)
Only Unique Records (1 - 2 , 2 - 1 are same so only one record will be returned which ever is the latest one)
Ordered by Descending based on message_id
SQL Query
The main purpose of this query is to get records of user_id to find out to whom he has sent messages and from whom he had received messages.
I have also attached the sheet for your reference.
Here is my try
WITH t
AS (SELECT *
FROM messages
WHERE user_id_sender = 1)
SELECT DISTINCT user_id_reciever,
*
FROM t;
WITH h
AS (SELECT *
FROM messages
WHERE user_id_reciever = 1)
SELECT DISTINCT user_id_sender,
*
FROM h;
;WITH tmpMsg AS (
SELECT M2.message_id
,M2.user_id_receiver
,M2.user_id_sender
,M2.message_date
,M2.message_details
,ROW_NUMBER() OVER (PARTITION BY user_id_receiver+user_id_sender ORDER BY message_date DESC) AS 'RowNum'
FROM messages M2
WHERE M2.user_id_receiver = 1
OR M2.user_id_sender = 1
)
SELECT T.message_id
,T.user_id_receiver
,T.user_id_sender
,T.message_date
,T.message_details
FROM tmpMsg T
WHERE RowNum <= 1
The above should fetch you the results you are looking for when you query for a particular user_id (replace the 1 with parameter e.g. #p_user_id). The user_id_receiver+user_id_sender in the PARTITION clause ensure that records with user id combinations such as 1 - 2, 2 - 1 are not selected twice.
Hope this helps.
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_receiver = 1
--order by message_date DESC
) T where T.rowno = 1
UNION ALL
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_sender = 1
-- order by message_date DESC
) T where T.rowno = 1
Explanation: For each group of user_id_sender, it orders internally by message_date desc, and then adds row numbers, and we only want the first one (chronologically last). Then do the same for user_id_receiver, and union the results together to get 1 result set with all the desired rows. You can then add your own order by clause and additional where conditions at the end as required.
Of course, this only works for any 1 user_id at a time (replace =1 with #user_id).
To get a result from all user_id's at once, is a totally different query, so I hope this helps?

get first row for each group

I want to transform this data
account; docdate; docnum
17700; 9/11/2015; 1
17700; 9/12/2015; 2
70070; 9/1/2015; 4
70070; 9/2/2015; 6
70070; 9/3/2015; 9
into this
account; docdate; docnum
17700; 9/12/2015; 2
70070; 9/3/2015; 9
.. for each account I want to have one row with the most current (=max(docdate)) docdate. I already tried different approaches with cross apply and row_number but couldn't achieve the desired results
Use ROW_NUMBER:
SELCT account, docdate, docnum
FROM (
SELECT account, docdate, docnum,
ROW_NUMBER() OVER (PARTITION BY account
ORDER BY docdate DESC) AS rn
FROM mytable ) AS t
WHERE t.rn = 1
PARTITION BY account clause creates slices of rows sharing the same account value. ORDER BY docdate DESC places the record having the maximum docdate value at the top of its related slice. Hence rn = 1 points to the record with the maximum docdate value within each account partition.

T-SQL order by, based on other column value

I'm stuck with a query which should be pretty simple but, for reasons unknown, my brain is not playing ball here ...
Table:
id(int) | strategy (varchar) | value (whatever)
1 "ABC" whatevs
2 "ABC" yeah
3 "DEF" hello
4 "DEF" kitty
5 "QQQ" hurrr
The query should select ALL rows grouped on strategy but only one row per strategy - the one with the higest id.
In the case above, it should return rows with id 2, 4 and 5
SELECT id, strategy , value
FROM (
SELECT id, strategy , value
,ROW_NUMBER() OVER (PARTITION BY strategy ORDER BY ID DESC) rn
FROM Table_Name
) Sub
WHERE rn = 1
Working SQL FIDDLE
You can use window function to get the solution you want. Fiddle here
with cte as
(
select
rank()over(partition by strategy order by id desc) as rnk,
id, strategy, value from myT
)
select id, strategy, value from
cte where rnk = 1;
Try this:
SELECT T2.id,T1.strategy,T1.value
FROM TableName T1
INNER JOIN
(SELECT MAX(id) as id,strategy
FROM TableName
GROUP BY strategy) T2
ON T1.id=T2.id
Result:
ID STRATEGY VALUE
2 ABC yeah
4 DEF kitty
5 QQQ hurrr
See result in SQL Fiddle.
SELECT id, strategy , value
FROM (
SELECT id, strategy , value
,MAX(id) OVER (PARTITION BY strategy) MaxId
FROM YourTable
) Sub
WHERE id=MaxId
You may try this one as well:
SELECT id, strategy, value FROM TableName WHERE id IN (
SELECT MAX(id) FROM TableName GROUP BY strategy
)
Bit depends on your data, you might get results faster with it as it does not do sorting, but by the other hand it uses IN, which can slow you down if there is many 'strategies'

Getting filtered results with subquery

I have a table with something like the following:
ID Name Color
------------
1 Bob Blue
2 John Yellow
1 Bob Green
3 Sara Red
3 Sara Green
What I would like to do is return a filtered list of results whereby the following data is returned:
ID Name Color
------------
1 Bob Blue
2 John Yellow
3 Sara Red
i.e. I would like to return 1 row per user. (I do not mind which row is returned for the particular user - I just need that the [ID] is unique.) I have something already that works but is really slow where I create a temp table adding all the ID's and then using a "OUTER APPLY" selecting the top 1 from the same table, i.e.
CREATE TABLE #tb
(
[ID] [int]
)
INSERT INTO #tb
select distinct [ID] from MyTable
select
T1.[ID],
T2.[Name],
T2.Color
from
#tb T1
OUTER APPLY
(
SELECT TOP 1 * FROM MyTable T2 WHERE T2.[ID] = T1.[ID]
) AS V2
DROP TABLE #tb
Can somebody suggest how I may improve it?
Thanks
Try:
WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS 'RowNo',
ID, Name, Color
FROM table
)
SELECT ID,Name,color
FROM CTE
WHERE RowNo = 1
or
select
*
from
(
Select
ID, Name, Color,
rank() over (partition by Id order by sum(Name) desc) as Rank
from
table
group by
ID
)
HRRanks
where
rank = 1
If you're using SQL Server 2005 or higher, you could use the Ranking functions and just grab the first one in the list.
http://msdn.microsoft.com/en-us/library/ms189798.aspx

Distinct with Count and SQl Server 2005

Trying to work on a query that will return the top 3 selling products with the three having a distinct artist. Im getting stuck on getting the unique artist.
Simplified Table schema
Product
ProductID
Product Name
Artist Name
OrderItem
ProductID
Qty
So results would look like this...
PID artist qty
34432, 'Jimi Hendrix', 6543
54833, 'stevie ray vaughan' 2344
12344, 'carrie underwood', 1
Use this:
with summed_sales_of_each_product as
(
select p.artist_name, p.product_id, sum(i.qty) as total
from product p join order_item i
on i.product_id = p.product_id
group by p.artist_name, p.product_id
),
each_artist_top_selling_product as
(
select x_in.artist_name, x_in.product_id, x_in.total
from summed_sales_of_each_product x_in where total =
(select max(x_out.total)
from summed_sales_of_each_product x_out
where x_out.artist_name = x_in.artist_name)
)
select top 3
artist_name, product_id, total
from each_artist_top_selling_product
order by total desc
But you cannot stop at that query, how about if there are two products on one artist that are ties on highest selling? This is how the data like this...
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
tomjones sexbomb 400
...will result to following using the above query:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
Which one to choose? yesterday or something? Since you cannot arbitrarily chose one over the other, you must list both. Also, what if the top 10 highest selling belongs to beatles and are ties, each with a quantity of 1000? Since that is the very best thing you are avoiding(i.e. reporting same artist on top 3), you have to amend the query so the top 3 report will look like this:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
To Amend:
with summed_sales_of_each_product as
(
select p.artist_name, p.product_id, sum(i.qty) as total
from product p join order_item i
on i.product_id = p.product_id
group by p.artist_name, p.product_id
),
each_artist_top_selling_product as
(
select x_in.artist_name, x_in.product_id, x_in.total
from summed_sales_of_each_product x_in
where x_in.total =
(select max(x_out.total)
from summed_sales_of_each_product x_out
where x_out.artist_name = x_in.artist_name)
),
top_3_total as
(
select distinct top 3 total
from each_artist_top_selling_product
order by total desc
)
select artist_name, product_id, total
from each_artist_top_selling_product
where total in (select total from top_3_total)
order by total desc
How about if the beatles has another product which has 900 qty? Will the above query still work? Yes, it will still work. Since the top_3 CTE only concerns itself from the already filtered top qty on each artist. So this source data...
beatles yesterday 1000
beatles something 1000
beatles and i love her 900
elvis jailbreak rock 800
nirvana lithium 600
tomjones sexbomb 400
...will still result to following:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
If I have understood your schema correctly, you should be able to do it like this:
select top 3 * from(
select p.ProductId, p.ArtistName, sum(o.qty) as qty from Product p, OrderItem o
where p.ProductId = o.ProductId
group by p.productId, p.ArtistName
order by sum(o.qty)
)
I don't know what you want to do if an Artist has two top-ranked products with identical sales--this will return two in case of a tie.
If you want to add another criteria, such as "most recent", you have to add that to both subqueries.
select top 3 sales_by_item.ProductID,
sales_by_item.Artist,
sales_by_item.Qty
from
(
select * from product x
inner join OrderItem y
on x.productid = y.productid
group by productid, Artist
) sales_by_item
inner join
(
select artist, max(qty) as maxqty
from product x
inner join OrderItem y
on x.productid = y.productid
group by artist
) max_by_artist
on sales_by_item.artist = max_by_artist.artist
and sales_by_item.qty = max_by_artist.maxqty
order by sales_by_item.qty
Edited to make subquery names more descriptive
Second attempt. I’m not in a position to test this code, and I’m not sure if I’ve got that “partition by” clause configured correctly. The idea is:
The inner query gets the sum of Qty for all product/artists, and uses the row_number() function to number them starting with the largest, and resets the ordering for each artist. (This can be done, but my syntax may be off.)
The outer query picks out the first (largest) item for each artist, and returns only the first three (ordere by Qty)
If an artists top two products tie for total Qty, I arbitrarily break the tie in favor of the “earliest” album.
(I try to avoid using "Top n", but it's late and I don't want to tackle another row_number() function.)
SELECT top 3
ProductId
,ArtistName
,Qty
from (-- Products + Artists by total qty
select
pr.ProductId
,pr.ArtistName
,sum(oi.Qty) Qty
,row_number() over (partition by pr.ArtistName order by pr.ArtistName, sum(oi.Qty) desc, pr.ProductId) Ranking
from Product pr
inner join OrderItem oi
on oi.ProductID = pr.ProductID
group by pr.ProductId, pr.ArtistName) BestSellers
where Ranking = 1
group by ProductId, ArtistName) BestArtists
order by Qty desc
Analyzing your request, it sounds like the results should be the highest product quantity for the top three artists. So, if Jimi Hendrix has the top 10 product quantities and Stevie Ray Vaughan is 11th, you want Jimi with his highest product then Stevie with his highest product.
With ProductRanksForArtists As
(
Select P.ProductId, P.ArtistName, Sum(O.Qty) As Total
, ROW_NUMBER OVER( PARTITION BY P.ArtistName ORDER BY Sum(O.Qty) DESC ) As ProductRank
From Product As P
Join OrderItem As O
On O.ProductId = P.ProductId
Group By P.ProductId, P.ArtistName
)
, HighestProductForArtists As
(
Select ProductId, ArtistName, Total
, ROW_NUMBER OVER( ORDER BY Total DESC ) As TotalRank
From ProductRanksForArtists
Where ProductRank = 1
)
Select ProductId, ArtistName, Total
From HighestProductForArtists
Where TotalRank <= 3
Try this
Select top 3 artist, count(artist) from tablename group by artist order by artist count(artist) desc

Resources