Fetch only one of each entry based on date - sql-server

Im currently working with SQL Compact 4 and Razor and im trying to fetch only one of each entry based on latest date
id Name Number LastDate
1 Joe 1111 2014-01-01
2 Sam 2222 2014-01-02
3 Joe 1111 2014-04-11
4 Sam 2222 2014-04-12
5 Lee 3333 2014-04-12
I'm trying to write the data out to a webgrid but i can't find the correct SQL statement to only load id 3, 4 and 5 since they are the last updated entries.

Try with
SELECT id, Name, Number, LastDate FROM yourTable t1 INNER JOIN
(SELECT Number, MAX(LastDate) AS MaxDate FROM yourTable GROUP BY Number) t2
ON t1.Number = t2.Number AND t1.LastDate = t2.MaxDate

You can do this with one query:
select t.*
from t
where not exists (select 1
from t t2
where t2.number = t.number and
t2.lastdate > t.lastdate
);
This implements the logic: "Get me all rows from t where there is no row with the same number and a later lastdate".

I think something like this should work (assuming you want the last record by the "Number" column and that there are no duplicate dates by number):
select max(lastdate) as lastdate, number into #temp
from t
group by number
select * from #temp t1
inner join t on t.number = t1.number and t.lastdate = t1.lastdate
Here is a SQL Fiddle: http://www.sqlfiddle.com/#!6/01357/5
(Thinking about this further, even if there were duplicate dates by number, the only field that would differ would be the id column which you could perhaps take the MAX of depending on your requirements.)

You can use row_number() in the OVER statement like this:
select id, name, number, lastdate
from
(
select
id, name, number, lastdate,
row_number() OVER (PARTITION BY Number ORDER BY LastDate DESC) as dateord
from table
) t
where t.dateord = 1

Related

Remove duplicate records from views in SQL Server

How to remove duplicate records from a view? I need to keep them on the physical table, but in the view, I don't want the duplicates
Here is the query I used:
CREATE VIEW myview
AS
SELECT DISTINCT *
FROM [roug].[dbo].[Table_1]
ORDER BY id
for the table :
id| name age
----------
c1 ann 12
u2 joe 15
c1 ann 12
c1 ann 12
u5 dev 13
u3 Jim 16
u3 Jim 16
You can either use DISTINCT or ROW_NUMBER() Like this
create view myview as
WITH CTE
AS
(
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY [Id],[Name],[Age] ORDER BY ID),
*
FROM [roug].[dbo].[Table_1]
)
SELECT
[Id],[Name],[Age]
FROM CTE
WHERE RN = 1
If you want to delete data then you should be doing it in the source table not the view. A standard approach for de-duping is via a cte. Try
;
WITH cte
AS (SELECT id
, name
, age
, ROW_NUMBER() OVER (PARTITION BY id, name, age ORDER BY id) RN
FROM Table_1
)
DELETE FROM cte
WHERE RN > 1
Depends on if you want to delete the actual data, or just not display it in the view.

Select highest value in a column with other column values SQL Server

My table looks like this:
And I want to get highest bid amount for a specific product, with the row id. My query is like this
SELECT
MAX(BidAmount) as highestBid,id
FROM
[wf_bid]
WHERE
ProductId = 101 AND ClientId = 101
GROUP BY
id
I expect only one row with highest BidAmount, but the query returns all rows with this product id and client id. How can I fix this issue?
How about sub-query ? If you have multiple records with same BidAmount, then it return top 1.
SELECT TOP 1
BidAmount as highestBid,id
FROM [wf_bid] WHERE BidAmount = (Select Max(BidAmount) FROM [wf_bid] WHERE ProductId=101 and ClientId=101)
You can use row_number() and select the first row:
SELECT *
FROM
(
SELECT
id,
BidAmount,
ROW_NUMBER() OVER (ORDER BY BidAmount desc) as rn
FROM
[wf_bid]
WHERE ProductId = 101 and ClientId = 101
) i
WHERE
i.rn = 1
How about this way:
SELECT id,highestBid from
(Select Max(BidAmount)highestBid,productID,clientid FROM [wf_bid] WHERE ProductId=101 and ClientId=101) a
LEFT JOIN
(SELECT id,productID,clientid FROM [wf_bid]) as b
where a.productID = b.productid and a.clientid = b.clientid
try this way,
select * FROM
(SELECT
id,
BidAmount,
ROW_NUMBER() OVER (parrtition by ProductId ORDER BY BidAmount desc) as rn
FROM
[wf_bid]
WHERE ClientId = 101)t4
where rn=1
Your problem is in the group by ID, it doesn't work that way because it isn't "adding your bids" it is telling you the max number of every ID not just which is the biggest bid and it's ID. I'm guessing you'll get what you want if you delete group by id. If not you would need to explain your need further.

How to get the latest entry for each item for in a Month with a single SQL query [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 6 years ago.
I am trying to write a query to pick one entry for each item for each month but the latest in the month from the following table:
Name | Date | Value
a |2015-01-01 | 1
a |2015-01-02 | 2
b |2015-01-03 | 1
b |2015-01-04 | 1
b |2015-01-03 | 3
c |2015-01-02 | 2
c |2015-01-29 | 10
a |2015-02-10 | 2
a |2015-02-20 | 1
c |2015-02-10 | 2
c |2015-02-22 | 23
b |2015-02-25 | 1
b |2015-02-19 | 2
return should be:
a |2015-01-02 | 2
b |2015-01-04 | 1
c |2015-01-29 | 10
a |2015-02-20 | 1
b |2015-02-25 | 1
c |2015-02-22 | 23
I wonder how would this be achieved instead of sending multiple queries to SQL server for each month I would like to load all the values with one query then filter the collection on the memory. Otherwise I would end up writing a query as below:
SELECT Name,Date, Value FROM MyTable mt
INNER JOIN (
select max(Date) as MaxDate
FROM [MyTable] m WHERE YEAR(Date) =YEAR(#date)
AND MONTH(Date)=MONTH(#date)) mx ON t.Date = mx.MaxDate)
And this query needs to be run for each month.
Any better idea to return all entries with a single query?
Thanks,
Try grouping by year and month in the derived table:
SELECT t1.Name, t1.[Date], t1.Value
FROM MyTable t1
INNER JOIN (
SELECT Name, YEAR(Date) AS y, MONTH([Date]) AS m, MAX([Date]) as MaxDate
FROM MyTable
GROUP BY Name, YEAR(Date), MONTH([Date])
) t2 ON t1.Name = t2.Name AND
YEAR(t1.[Date]) = t2.y AND MONTH(t1.[Date]) = t2.m AND
t1.[Date] = t2.MaxDate
SELECT *
FROM (
SELECT NAME, DATE, VALUE,
ROW_NUMBER() OVER (PARTITION BY NAME, YEAR(Date), MONTH(Date)
ORDER BY Date DESC) rn
FROM MyTable) AS t
WHERE t.rn = 1
Assuming that you are using a SQL Server version that supports it, you can use the ROW_NUMBER() windowing function to return a sequence number for each row, then you can subsequently use that to restrict to only the rows that you require.
SELECT [Name],[Date],[Value]
,ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Date] DESC) AS [Seq]
FROM myTable
Things to consider:
What happens when there is a tie? ROW_NUMBER will always return a sequence number, but if your data has > 1 row at the same Date value, the order will be arbritrary. To solve this add additional tie-break ORDER BY entries
How do I filter this? Put it into a Common Table Expression, Inline View or Real View
I think you need a correlated query once you have a set of distinct (Name, Month). There are various ways of doing this, one is to use cross apply:
select *
from (select distinct Name, Month(Date) as Month
from theTable) itemMonths
cross apply (select Max(value)
from theTable t
where Month(t.Date) = itemMonths.Month
and t.Name = itemMonths.Name)
You could try the following:
WITH MyTable AS
(SELECT 'a' AS name, GETDATE() AS date, 1 AS value
UNION ALL
SELECT 'a', GETDATE()+1, 2
)
, res AS (
SELECT Name,date,MAX(Date) OVER(PARTITION BY Name, DATEPART(yyyy,date), DATEPART(mm, date)) AS max_date , Value FROM MyTable
)
SELECT name,date,res.value FROM res WHERE date=max_date
You still need a filter though as the Max Over will return all rows.
If you were using Teradata I'd suggest using the Qualify Clause but Itzik hasn't had any luck getting this ported to SQL server!
https://connect.microsoft.com/SQLServer/feedback/details/532474
Use Cross apply
SELECT b.*
FROM mytable mt
CROSS apply (SELECT TOP 1 NAME, date, value
FROM [mytable] m
WHERE m.NAME = mt.NAME
AND Month(m.date) = Month(mt.date)
AND Year(m.date) = Year(mt.date)
ORDER BY m.date DESC) b

SQL Server - Select most recent records with condition

I have a table like this.
Table :
ID EnrollDate ExitDate
1 4/1/16 8/30/16
2 1/1/16 null
2 1/1/16 7/3/16
3 2/1/16 8/1/16
3 2/1/16 9/1/16
4 1/1/16 12/12/16
4 1/1/16 12/12/16
4 1/1/16 12/12/16
4 1/1/16 null
5 5/1/16 11/12/16
5 5/1/16 11/12/16
5 5/1/16 11/12/16
Need to select the most recent records with these conditions.
One and only one record has the most recent enroll date - select that
Two or more share same most recent enroll date and one and only one record has either a NULL Exit Date or the most recent Exit Date - Select the record with null. If no null record pick the record with recent exit date
Two or more with same enroll and Exit Date - If this case exists, don't select those record
So the expected result for the above table should be :
ID EnrollDate ExitDate
1 4/1/16 8/30/16
2 1/1/16 null
3 2/1/16 9/1/16
4 1/1/16 null
I wrote the query with group by. I am not sure how to select with the conditions 2 and 3.
select t1.* from table t1
INNER JOIN(SELECT Id,MAX(EnrollDate) maxentrydate
FROM table
GROUP BY Id)t2 ON EnrollDate = t2.maxentrydate and t1.Id=t2.Id
Please let me know what is the best way to do this.
Using the rank() window function, I think it's possible.
This is untested, but it should work:
select t.ID, t.EnrollDate, t.ExitDate
from (select t.*,
rank() over(
partition by ID
order by EnrollDate desc,
case when ExitDate is null then 1 else 2 end,
ExitDate desc) as rnk
from tbl t) t
where t.rnk = 1
group by t.ID, t.EnrollDate, t.ExitDate
having count(*) = 1
The basic idea is that the rank() window function will rank the most "recent" rows with a value of 1, which we filter on in the outer query's where clause.
If more than one row have the same "most recent" data, they will all share the same rank of 1, but will get filtered out by the having count(*) = 1 clause.
Use ROW_NUMBER coupled with CASE expression to achieve the desired result:
WITH Cte AS(
SELECT t.*,
ROW_NUMBER() OVER(
PARTITION BY t.ID
ORDER BY
t.EnrollDate DESC,
CASE WHEN t.ExitDate IS NULL THEN 0 ELSE 1 END,
t.ExitDate DESC
) AS rn
FROM Tbl t
INNER JOIN (
SELECT
ID,
COUNT(DISTINCT CHECKSUM(EnrollDate, ExitDate)) AS DistinctCnt, -- Count distinct combination of EnrollDate and ExitDate per ID
COUNT(*) AS RowCnt -- Count number of rows per ID
FROM Tbl
GROUP BY ID
) a
ON t.ID = a.ID
WHERE
(a.DistinctCnt = 1 AND a.RowCnt = 1)
OR a.DistinctCnt > 1
)
SELECT
ID, EnrollDate, ExitDate
FROM Cte c
WHERE Rn = 1
The ORDER BY clause in the ROW_NUMBER takes care of conditions 2 and 3.
The INNER JOIN and the WHERE clause take care of 1 and 4.
ONLINE DEMO
with B as (
select id, enrolldate ,
exitdate,
row_number() over (partition by id order by enrolldate desc, case when exitdate is null then 0 else 1 end, exitdate desc) rn
from ab )
select b1.id, b1.enrolldate, b1.exitdate from b b1
left join b b2
on b1.rn = b2.rn -1 and
b1.id = b2.id and
b1.exitdate = b2.exitdate and
b1.enrolldate = b2.enrolldate
where b1.rn = 1 and
b2.id is nULL
The left join is used to fullfill the 3) requirement. When record is returned then we don't want it.

Join two tables with conditions depending on multiples columns

In SQL Server 2008, I want to join two table on key that might have duplicate, but the match is unique with the information from other columns.
For a simplified purchase record example,
Table A:
UserId PayDate Amount
1 2015 100
1 2010 200
2 2014 150
Table B:
UserId OrderDate Count
1 2009 4
1 2014 2
2 2013 5
Desired Result:
UserId OrderDate PayDate Amount Count
1 2009 2010 200 4
1 2014 2015 100 2
2 2013 2014 150 5
It's guaranteed that:
Table A and Table B have same number of rows, and UserId in both table are same set of numbers.
For any UserId, PayDate is always later than OrderDate
Rows with same UserId are matched by sorted sequence of Date. For example, Row 1 in Table A should match Row 2 in Table B
My idea is that on both tables, first sort by Date, then add another Id column, then join on this Id column. But I not authorized to write anything into the database. How can I do this task?
Row_Number() will be your friend here. It allows you to add a virtual sequencing to your resultset.
Run this and study the output:
SELECT UserID
, OrderDate
, "Count" As do_not_use_reserved_words_for_column_names
, Row_Number() OVER (PARTITION BY UserID ORDER BY OrderDate) As sequence
FROM table_b
The PARTITION BY determines when the counter should be "reset" i.e. it should restart after a change of UserID
The ORDER BY, well, you've guessed it - determines the order of the sequence!
Pull this all together:
; WITH payments AS (
SELECT UserID
, PayDate
, Amount
, Row_Number() OVER (PARTITION BY UserID ORDER BY PayDate) As sequence
FROM table_b
)
, orders AS (
SELECT UserID
, OrderDate
, "Count" As do_not_use_reserved_words_for_column_names
, Row_Number() OVER (PARTITION BY UserID ORDER BY OrderDate) As sequence
FROM table_b
)
SELECT orders.UserID
, orders.OrderDate
, orders.do_not_use_reserved_words_for_column_names
, payments.PayDate
, payments.Amount
FROM orders
LEFT
JOIN payments
ON payments.UserID = orders.UserID
AND payments.sequence = orders.sequence
P.S. I've opted for an outer join because I assumed that there's not always going to be a payment for every order.
Try:
;WITH t1
AS
(
SELECT UserId, PayDate, Amount,
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY PayDate) AS RN
FROM TableA
),
t2
AS
(
SELECT UserId, OrderDate, [Count],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY OrderDate) AS RN
FROM TableB
)
SELECT t1.UserId, t2.OrderDate, t1.PayDate, t1.Amount, t2.[Count]
FROM t1
INNER JOIN t2
ON t1.UserId = t2.UserId AND t1.RN = t2.RN

Resources