How to bring the last 5 register of each ID with SQL? - sql-server

I have a table with the purchase registers. There are all the purchase registers of a Pet Shop, since 2010.
I need some help to bring only the last five purchase of each client.
I was trying, but it is not working. It brings me the last 5 registers of all the table, and not of each client.
SELECT TOP (5) [client_name],
[purchase_date],
[item]
FROM [Pet_Shop]
ORDER BY client_name
WHERE client_name in ('John', 'Mary', 'Austin')
I need this kind of return:
client_name | purchase_date | item
___________________________________
John | 2019-09-14 | food
John | 2019-09-13 | ball
John | 2019-09-12 | shampoo
John | 2019-09-11 | cookie
John | 2019-09-11 | food
Mary | 2019-09-14 | collar
Mary | 2019-07-14 | food
Mary | 2019-06-14 | toy
Mary | 2019-06-14 | hamster
Mary | 2019-05-14 | food
Austin | 2019-09-18 | food
Austin | 2019-09-11 | collar
Austin | 2019-09-10 | toy
Austin | 2019-09-09 | catnip
Austin | 2019-09-11 | food

Use ROW_NUMBER():
SELECT *
FROM (
SELECT
client_name,
purchase_date,
item,
ROW_NUMBER() OVER(PARTITION BY client_name ORDER BY purchase_date desc) rn
FROM Pet_Shop
WHERE client_name in ('John', 'Mary', 'Austin')
) x
WHERE rn <= 5
ORDER BY client_name

You can use CROSS APPLY like this:
DECLARE #Registry TABLE (client_name VARCHAR(100), purchase_date DATETIME, item INT)
INSERT INTO #Registry
(client_name, purchase_date, item)
VALUES
('Client1', '1/1/2019', 1),
('Client1', '2/1/2019', 2),
('Client1', '3/1/2019', 3),
('Client1', '4/1/2019', 4),
('Client1', '5/1/2019', 5),
('Client1', '6/1/2019', 6),
('Client1', '7/1/2019', 7),
('Client2', '1/1/2019', 1),
('Client2', '2/1/2019', 2),
('Client2', '3/1/2019', 3),
('Client2', '4/1/2019', 4),
('Client2', '5/1/2019', 5),
('Client2', '6/1/2019', 6),
('Client2', '7/1/2019', 7)
;WITH Clients AS (
SELECT client_name FROM #Registry GROUP BY client_name
)
SELECT C.*, P.purchase_date, P.item
FROM Clients AS C
CROSS APPLY
(
SELECT TOP 5 R.purchase_date, R.item
FROM #Registry R
WHERE R.client_name = C.client_name
ORDER BY R.purchase_date DESC
) P
ORDER BY C.client_name, P.purchase_date, P.item
Here is the result:
client_name purchase_date item
Client1 2019-03-01 00:00:00.000 3
Client1 2019-04-01 00:00:00.000 4
Client1 2019-05-01 00:00:00.000 5
Client1 2019-06-01 00:00:00.000 6
Client1 2019-07-01 00:00:00.000 7
Client2 2019-03-01 00:00:00.000 3
Client2 2019-04-01 00:00:00.000 4
Client2 2019-05-01 00:00:00.000 5
Client2 2019-06-01 00:00:00.000 6
Client2 2019-07-01 00:00:00.000 7

Related

SQL Server : deleting records with similar time

I would like to delete records that have same (or similar) time. This is the sample data:
ID | Name | DateTime
---+------+--------------------
1 | Joe | 2017-03-01 11:33:13
1 | Joe | 2017-03-01 11:33:14
1 | Joe | 2017-03-01 11:33:15
1 | Joe | 2017-03-01 11:55:30
2 | John | 2017-02-15 08:55:48
2 | John | 2017-02-15 08:55:49
2 | John | 2017-02-15 08:56:30
2 | John | 2017-02-15 10:15:40
After deleting:
ID | Name | DateTime
---+------+---------------------
1 | Joe | 2017-03-01 11:33:13
1 | Joe | 2017-03-01 11:55:30
2 | John | 2017-02-15 08:55:48
2 | John | 2017-02-15 10:15:40
Deletes all similar times (e.g. 10 minutes) for users
Please help me how to do it. Thanks
CREATE TABLE #TABLE1
([ID] INT, [NAME] VARCHAR(4), [DATETIME] DATETIME)
INSERT INTO #TABLE1
([ID], [NAME], [DATETIME])
VALUES
(1, 'JOE', '2017-03-01 11:33:13'),
(1, 'JOE', '2017-03-01 11:33:14'),
(1, 'JOE', '2017-03-01 11:33:15'),
(1, 'JOE', '2017-03-01 11:55:30'),
(2, 'JOHN', '2017-02-15 08:55:48'),
(2, 'JOHN', '2017-02-15 08:55:49'),
(2, 'JOHN', '2017-02-15 08:56:30'),
(2, 'JOHN', '2017-02-15 10:15:40')
SELECT A.ID,A.NAME,A.[DATETIME] FROM
(SELECT *,ROW_NUMBER() OVER( PARTITION BY ( CAST(CONVERT(CHAR(16), [DATETIME],113) AS DATETIME)) ORDER BY [NAME]) AS RN FROM #TABLE1
)A
WHERE RN=1 ORDER BY ID
output
ID NAME DATETIME
1 Joe 2017-03-01 11:33:13.000
1 Joe 2017-03-01 11:55:30.000
2 John 2017-02-15 08:55:48.000
2 John 2017-02-15 10:15:40.000

SQL Server parent child (parent see all everything)?

This is my table:
EmployeeID Employee ManagerID
---------------------------------
1 Anna 5
2 John 4
3 Steve 4
4 Lisa 1
5 Adam NULL
6 Per 1
There is no problem for me to get parent and child relationship with a self-join like this:
SELECT
E.EmployeeID,
E.Employee AS Employee,
E.ManagerID,
M.Employee AS Manager
FROM
Employee AS E
LEFT JOIN
Employee AS M ON E.ManagerID = M.EmployeeID
EmployeeID Employee ManagerID Manager
1 Anna 5 Adam
2 John 4 Lisa
3 Steve 4 Lisa
4 Lisa 1 Anna
5 Adam NULL NULL
6 Per 1 Anna
However, How would i go about to make sure that the parent see the whole hierarchy level?
I would like the table to look like this:
EmployeeID Manager Employee EmployeeID
5 Adam Anna 1
5 Adam Per 6
5 Adam Lisa 4
5 Adam John 2
5 Adam Steve 3
1 Anna Per 6
1 Anna Lisa 4
1 Anna John 2
1 Anna Steve 3
4 Lisa John 2
4 Lisa Steve 3
Note: in this example i only have 3 levels of manger but there can be many more
You can try this:
DECLARE #DataSource TABLE
(
[EmployeeID] TINYINT
,[Employee] VARCHAR(12)
,[ManagerID] TINYINT
);
INSERT INTO #DataSource ([EmployeeID], [Employee], [ManagerID])
VALUES (1, 'Anna', 5)
,(2, 'John', 4)
,(3, 'Steve', 4)
,(4, 'Lisa', 1)
,(5, 'Adam', NULL)
,(6, 'Per', 1);
WITH DataSource AS
(
SELECT DISTINCT DS1.*
,0 AS [Level]
,DS1.[EmployeeID] AS Parent
FROM #DataSource DS1
INNER JOIN #DataSource DS2
ON DS1.[EmployeeID] = DS2.[ManagerID]
UNION ALL
SELECT DS2.*
,DS1.[Level] + 1
,DS1.Parent
FROM DataSource DS1
INNER JOIN #DataSource DS2
ON DS1.[EmployeeID] = DS2.[ManagerID]
)
SELECT DS1.[EmployeeID]
,DS1.[Employee] AS [Manager]
,DS.[EmployeeID]
,DS.[Employee]
FROM DataSource DS
INNER JOIN #DataSource DS1
ON DS.[Parent] = DS1.[EmployeeID]
WHERE DS.[Level] <> 0
ORDER BY DS.[Parent] DESC;
We are using recursive CTE and it may look a kind of messy and complicated if you are seeing this syntax for the first time, but it's nothing special.
When are using recursive CTE run some performance tests in order to be sure it is the right technique for solving your issue.
You should use recursive CTE syntax. In the first iteration (before UNION ALL) you get all Parent-Child pairs. In the recursive part (after UNION ALL) you get the next level child for each pair and substitute it into the pair Parent-Child instead of the Child leaving Parent the same.
WITH CTE AS
(
SELECT TP.EmployeeID as ManagerId,
TP.Employee as Manager,
TC.EmployeeID as EmployeeID,
TC.Employee as Employee
FROM TEmployee as TP
JOIN TEmployee as TC on (TP.EmployeeID = TC.ManagerID)
UNION ALL
SELECT TP.ManagerId as ManagerId,
TP.Manager as Manager,
TC.EmployeeID as EmployeeID,
TC.Employee as Employee
FROM CTE as TP
JOIN TEmployee as TC on (TP.EmployeeID = TC.ManagerID)
)
SELECT * FROM CTE Order By ManagerID
result:
+-----------+---------+------------+----------+
| ManagerId | Manager | EmployeeID | Employee |
+-----------+---------+------------+----------+
| 1 | Anna | 4 | Lisa |
| 1 | Anna | 6 | Per |
| 1 | Anna | 2 | John |
| 1 | Anna | 3 | Steve |
| 4 | Lisa | 2 | John |
| 4 | Lisa | 3 | Steve |
| 5 | Adam | 1 | Anna |
| 5 | Adam | 4 | Lisa |
| 5 | Adam | 6 | Per |
| 5 | Adam | 2 | John |
| 5 | Adam | 3 | Steve |
+-----------+---------+------------+----------+

DateDiff of Logtable Dates Which Have The Same Column in SQL Server

Using SQL Server 2012 I need to get the datediff of all dates in a Log table which has the same column, for example:
ID | Version | Status | Date
-----------------------------------------------------
12345 | 1 | new | 2014-05-01 00:00:00.000
12345 | 2 | up | 2014-05-02 00:00:00.000
12345 | 3 | appr | 2014-05-03 00:00:00.000
67890 | 1 | new | 2014-05-04 00:00:00.000
67890 | 2 | up | 2014-05-08 00:00:00.000
67890 | 3 | rej | 2014-05-13 00:00:00.000
I need to get the date diff of all sequential dates (date between 1, 2 and date between 2, 3)
I have tried creating a while but with no luck!
Your help is really appreciated!
This Calculates DateDiff as per your query "date diff of all sequential dates",if not sequential,it will just show same date.Further please don't use Reserved Keywords as Column names
SELECT ID,
[VERSION],
[STATUS],
[DATE],
CASE WHEN LEAD([DATE]) OVER (PARTITION BY ID ORDER BY [VERSION])=DATEADD(DAY,1,[DATE])
THEN CAST(DATEDIFF(DAY,[DATE],LEAD([DATE]) OVER (PARTITION BY ID ORDER BY VERSION)) AS VARCHAR(5))
ELSE [DATE] END AS DATEDIFFF
FROM
#TEMP
Another way with OUTER APPLY (get the previous value) :
SELECT t.*,
DATEDIFF(day,p.[Date],t.[Date]) as dd
FROM YourTable t
OUTER APPLY (
SELECT TOP 1 *
FROM YourTable
WHERE ID = t.ID AND [DATE] < t.[Date] AND [Version] < t.[Version]
ORDER BY [Date] DESC
) as p
Output:
ID Version Status Date dd
12345 1 new 2014-05-01 00:00:00.000 NULL
12345 2 up 2014-05-02 00:00:00.000 1
12345 3 appr 2014-05-03 00:00:00.000 1
67890 1 new 2014-05-04 00:00:00.000 NULL
67890 2 up 2014-05-08 00:00:00.000 4
67890 3 rej 2014-05-13 00:00:00.000 5
Note: If you are using SQL Server 2012 then better use LEAD and LAG functions.

skip records based on columns condition

I have a question in sql server
table name : Emp
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
1 |100 | Ravi |Kumar | 2
2 |101 | Jaid |Balu | 10
1 |100 | Hari | Babu | 5
1 |103 | nani | Jai |44
1 |103 | Nani | Balu |10
3 |103 |bani |lalu |20
Here need to retrieve unique records based on id and Pid columns and records which have duplicate records need to skip.
Finally I want output like below
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
2 |101 | Jaid |Balu | 10
3 |103 |bani |lalu |20
I found duplicate records based on below query
select id,pid,count(*) from emp group by id,pid having count(*) >=2
this query get duplicated records 2 that records need to skip to retrieve output
please tell me how to write query to achieve this task in sql server.
Since your output is based on unique ID and PID which do not have any duplicate value, You can use COUNT with partition to achieve your desired result.
SQL Fiddle
Sample Data
CREATE TABLE Emp
([Id] int, [Pid] int, [Firstname] varchar(4), [LastName] varchar(5), [Level] int);
INSERT INTO Emp
([Id], [Pid], [Firstname], [LastName], [Level])
VALUES
(1, 101, 'Ram', 'Kumar', 3),
(1, 100, 'Ravi', 'Kumar', 2),
(2, 101, 'Jaid', 'Balu', 10),
(1, 100, 'Hari', 'Babu', 5),
(1, 103, 'nani', 'Jai', 44),
(1, 103, 'Nani', 'Balu', 10),
(3, 103, 'bani', 'lalu', 20);
Query
SELECT *
FROM
(
SELECT *,rn = COUNT(*) OVER(PARTITION BY ID,PID)
FROM Emp
) Emp
WHERE rn = 1
Output
| Id | Pid | Firstname | LastName | Level |
|----|-----|-----------|----------|-------|
| 1 | 101 | Ram | Kumar | 3 |
| 2 | 101 | Jaid | Balu | 10 |
| 3 | 103 | bani | lalu | 20 |

How to calculate SUM balance of all accounts in T-SQL?

I have this table and data
CREATE TABLE #transactions (
[transactionId] [int] NOT NULL,
[accountId] [int] NOT NULL,
[dt] [datetime] NOT NULL,
[balance] [smallmoney] NOT NULL,
CONSTRAINT [PK_transactions_1] PRIMARY KEY CLUSTERED
( [transactionId] ASC)
)
INSERT #transactions ([transactionId], [accountId], [dt], [balance]) VALUES
(1, 1, CAST(0x0000A13900107AC0 AS DateTime), 123.0000),
(2, 1, CAST(0x0000A13900107AC0 AS DateTime), 192.0000),
(3, 1, CAST(0x0000A13A00107AC0 AS DateTime), 178.0000),
(4, 2, CAST(0x0000A13B00107AC0 AS DateTime), 78.0000),
(5, 2, CAST(0x0000A13D011D1860 AS DateTime), 99.0000),
(6, 2, CAST(0x0000A13F00000000 AS DateTime), 97.0000),
(7, 1, CAST(0x0000A13D0141E640 AS DateTime), 201.0000),
(8, 3, CAST(0x0000A1420094DD60 AS DateTime), 4000.0000),
(9, 3, CAST(0x0000A14300956A00 AS DateTime), 4100.0000),
(10, 3, CAST(0x0000A14700000000 AS DateTime), 4200.0000),
(11, 2, CAST(0x0000A14B00B84BB0 AS DateTime), 110.0000)
I need two queries.
For each transaction, I want to return in a query the most recent balance for each account, and an extra column with a SUM of each account balance at that point in time.
Same as 1 but grouped by date without the time portion. So the latest account balance at the end of each day (where there is a transaction in any account) for each account, but SUMed together as in 1.
Data above is sample data that I just made up, but my real table has hundreds of rows and ten accounts (which may increase soon). Each account has a unique accountId. Seems quite a tricky piece of SQL.
EXAMPLE
For 1. I need a result like this:
+---------------+-----------+-------------------------+---------+-------------+
| transactionId | accountId | dt | balance | sumBalances |
+---------------+-----------+-------------------------+---------+-------------+
| 1 | 1 | 2013-01-01 01:00:00.000 | 123 | 123 |
| 2 | 1 | 2013-01-01 01:00:00.000 | 192 | 192 |
| 3 | 1 | 2013-01-02 01:00:00.000 | 178 | 178 |
| 4 | 2 | 2013-01-03 01:00:00.000 | 78 | 256 |
| 5 | 2 | 2013-01-05 17:18:00.000 | 99 | 277 |
| 7 | 1 | 2013-01-05 19:32:00.000 | 201 | 300 |
| 6 | 2 | 2013-01-07 00:00:00.000 | 97 | 298 |
| 8 | 3 | 2013-01-10 09:02:00.000 | 4000 | 4298 |
| 9 | 3 | 2013-01-11 09:04:00.000 | 4100 | 4398 |
| 10 | 3 | 2013-01-15 00:00:00.000 | 4200 | 4498 |
| 11 | 2 | 2013-01-19 11:11:00.000 | 110 | 4511 |
+---------------+-----------+-------------------------+---------+-------------+
So, for transactionId 8, I take the latest balance for each account in turn and then sum them. AccountID 1: is 201, AccountId 2 is 97 and AccountId 3 is 4000. Therefore the result for transactionId 8 will be 201+97+4000 = 4298. When calculating the set must be ordered by dt
For 2. I need this
+------------+-------------+
| date | sumBalances |
+------------+-------------+
| 01/01/2013 | 192 |
| 02/01/2013 | 178 |
| 03/01/2013 | 256 |
| 05/01/2013 | 300 |
| 07/01/2013 | 298 |
| 10/01/2013 | 4298 |
| 11/01/2013 | 4398 |
| 15/01/2013 | 4498 |
| 19/01/2013 | 4511 |
+------------+-------------+
So on date 15/01/2013 the latest account balance for each account in turn (1,2,3) is 201,97,4200. So the result for that date would be 201+97+4200 = 4498
This gives your first desired resultset (SQL Fiddle)
WITH T
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions)
SELECT transactionId,
accountId,
dt,
balance,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T
ORDER BY dt;
It subtracts the current balance of the account from the previous balance to get the net difference then calculates a running total of those differences.
And that can be used as a base for your second result
WITH T1
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions),
T2 AS (
SELECT transactionId,
accountId,
dt,
balance,
ROW_NUMBER() OVER (PARTITION BY CAST(dt AS DATE) ORDER BY dt DESC, transactionId DESC) AS RN,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T1)
SELECT CAST(dt AS DATE) AS [date], sumBalances
FROM T2
WHERE RN=1
ORDER BY [date];
Part 1
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY dt, transactionId)
FROM b
ORDER BY dt
Part 2
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
, c AS (
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY CAST(dt AS DATE))
, r1 = ROW_NUMBER()OVER(PARTITION BY accountId, CAST(dt AS DATE) ORDER BY dt DESC)
FROM b
)
SELECT dt = CAST(dt AS DATE)
, sumBalance
FROM c
WHERE r1 = 1
ORDER BY CAST(dt AS DATE)

Resources