Select row satisfying certain condition and rows next to it - sql-server

Let's say I have a historical table keeping who has modified data
-------------------------------------------------------------
| ID | Last_Modif | User_Modif | Col3, Col4...
-------------------------------------------------------------
| 1 | 2018-04-09 12:12:00 | John
| 2 | 2018-04-09 11:10:00 | Jim
| 3 | 2018-04-09 11:05:00 | Mary
| 4 | 2018-04-09 11:00:00 | John
| 5 | 2018-04-09 10:56:00 | David
| 6 | 2018-04-09 10:53:00 | John
| 7 | 2018-04-08 19:50:00 | Eric
| 8 | 2018-04-08 18:50:00 | Chris
| 9 | 2018-04-08 15:50:00 | John
| 10 | 2018-04-08 12:50:00 | Chris
----------------------------------------------------------
I would like to find the modifs done by John and previous version before he did that, to check what he had modified. For example in this scenario I would like to return row 1,2,4,5,6,7,9,10
I am thinking of ranking first based on Last_modif then do a join to pick up the next row, but somehow the result is not correct. This seems not a LAG/LEAD case since I am not picking a single value from the next row, but instead the whole next row. Any idea ?
-- sample 1000 rows with RowNumber
with TopRows as
(select top 1000 *, ROW_NUMBER() OVER(ORDER BY Last_modif desc) RowNum from [Table])
--Reference rows : Rows modif by John
, ModifByJohn as
(Select * from TopRows where USER_MODIF = 'John')
select * from ModifByJohn
UNION
select ModifByNext.* from ModifByJohn join TopRows ModifbyNext on ModifByJohn.RowNum + 1 = ModifByNext.RowNum
order by RowNum
How will the code look like if we would like to return last 2 modifs before John did instead of 1 ?

Maybe you can take advantage of your current ID:
with x as
(
select t1.*,
(select top 1 id from tbl where id > t1.id) prev_id
from tbl t1
where t1.User_Modif = 'John'
)
select * from x;
GO
ID | Last_Modif | User_Modif | prev_id
-: | :------------------ | :--------- | ------:
1 | 09/04/2018 12:12:00 | John | 2
4 | 09/04/2018 11:00:00 | John | 5
6 | 09/04/2018 10:53:00 | John | 7
9 | 08/04/2018 15:50:00 | John | 10
with x as
(
select t1.*,
(select top 1 id from tbl where id > t1.id) prev_id
from tbl t1
where t1.User_Modif = 'John'
)
select ID, Last_Modif, User_Modif from x
union all
select ID, Last_Modif, User_Modif
from tbl
where ID in (select prev_id from x)
order by ID
GO
ID | Last_Modif | User_Modif
-: | :------------------ | :---------
1 | 09/04/2018 12:12:00 | John
2 | 09/04/2018 11:10:00 | Jim
4 | 09/04/2018 11:00:00 | John
5 | 09/04/2018 10:56:00 | David
6 | 09/04/2018 10:53:00 | John
7 | 08/04/2018 19:50:00 | Eric
9 | 08/04/2018 15:50:00 | John
10 | 08/04/2018 12:50:00 | Chris
dbfiddle here

Related

Sum up articles per orderID via SQL Server

I have the following:
OrderID | Articlenumber|
--------+--------------+
1 | 123 |
2 | 222 |
1 | 799 |
1 | 987 |
2 | 444 |
3 | 212 |
2 | 222 |
1 | 898 |
and I want the following (sum up all article numbers per orderID):
orderID|articelnumber |
-------+--------------------+
1 |123, 799, 987, 898 |
2 |222, 444, 222 |
3 |212 |
or:
orderID|articelnumber|articelnumber|articelnumber |articelnumber |
-------+-------------+-------------+--------------+--------------+
1 |123 |799 |987 | 898 |
2 |222 |444 |222 | |
3 |212 | | | |
How can I do it with SQL-Server? The number of articles per orderID is variable.
Thanks a lot!
Is something like this what you want?
;WITH cte AS
(
SELECT OrderID, Articlenumber
FROM [YOUR_TABLE]
)
SELECT
OrderID,
STUFF((SELECT ',' + Articlenumber FROM [YOUR_TABLE] WHERE [YOUR_TABLE].OrderID = cte.OrderID FOR XML PATH('')), 1, 1, '') articelnumber
FROM cte
GROUP BY OrderID
ORDER BY 1

How to find difference between two tables in MSSQL

I have got two tables 'Customer'.
The first one:
ID | UserID | Date
1. | 1 | 2018-05-01
2. | 1 | 2018-05-02
The second one:
ID | UserID | Date
1. | 1 | 2018-05-01
2. | 1 | 2018-05-02
3. | 1 | 2018-05-03
So, as you can see in the second table, there is one row more.
I have written so far this code:
;with cte_table1 as (
select UserID, count(id) cnt from db1.Customer group by UserID
),
cte_table2 as (
select UserID, count(id) cnt from db2.Customer group by UserID
)
select * from cte_table1 t1
join cte_table2 t2 on t2.UserID = t1.UserID
where t1.cnt <> t2.cnt
and this gives me expected result:
UserID | cnt | UserID | cnt
1 | 2 | 1 | 3
And so far, everything is fine. The thing is, these two tables have many rows and I'd like to have result with dates, where cnt does not match.
In other words, I'd like to have something like this:
UserID | cnt | Date | UserID | cnt | Date
1 | 2 | 2018-05-01 | 1 | 3 | 2018-05-01
1 | 2 | 2018-05-02 | 1 | 3 | 2018-05-01
1 | 2 | NULL | 1 | 3 | 2018-05-03
The best soulution would be resultset where both cte's are joined to give this:
UserID | cnt | Date | UserID | cnt | Date
1 | 2 | 2018-05-01 | 1 | 3 | 2018-05-01
1 | 2 | 2018-05-02 | 1 | 3 | 2018-05-01
1 | 2 | NULL | 1 | 3 | 2018-05-03
1 | 2 | 2018-05-30 | 1 | 3 | NULL
You should do a FULL OUTER JOIN query like below
Select
C1.UserID,
C1.cnt,
C1.Date,
C2.UserID,
C2.cnt,
C2.Date
from
db1.Customer C1
FULL OUTER JOIN
db2.Customer C2
on C1.UserId=C2.UserId and C1.date=C2.Date

SQL Server - get values from X months ago according to columndata

Let's say I have the following table (data is completely fiction):
ID | MonthDate | PersonID | Name | Status | MonthsAgoSinceLastCheck
1 | 2017-12 | 900 | Jack | Ill | -
2 | 2018-01 | 900 | Jack | Ill | 1
3 | 2018-02 | 900 | Jack | Ill | 2
4 | 2018-03 | 900 | Jack | Healthy | 1
5 | 2017-02 | 901 | Bill | Ill | -
6 | 2017-03 | 901 | Bill | Ill | 1
7 | 2017-05 | 901 | Bill | Healthy | 1
For each record, I would like to see the previous status that person had X months ago since last check (column MonthsAgoSinceLastCheck). Notice that MonthDate can skip months.
So in this case, the result would be
ID | MonthDate | PersonID | Name | Status | MonthsAgoSinceLastCheck | PreviousSatus
1 | 2017-12 | 900 | Jack | Ill | - | -
2 | 2018-01 | 900 | Jack | Ill | 1 | Ill
3 | 2018-02 | 900 | Jack | Ill | 2 | Ill
4 | 2018-03 | 900 | Jack | Healthy | 1 | Ill
5 | 2017-02 | 901 | Bill | Healthy | - | -
6 | 2017-03 | 901 | Bill | Healthy | 1 | Healthy
7 | 2017-05 | 901 | Bill | Ill | 2 | Healthy
Any sugestions/tips? I tried to do this with CTE's and self-joins but failed on both.
It's way easier to use full dates than year and months separately. The first thing you should do is generate a full date from your year + month. Then just self join with previous month, depending on the last check.
;WITH DataWithDates AS
(
SELECT
T.ID,
MonthDate = CONVERT(DATE, T.MonthDate + '-01'),
T.PersonID,
T.Name,
T.Status,
T.MonthsAgoSinceLastCheck
FROM
YourTable AS T
)
SELECT
D.ID,
D.MonthDate,
D.PersonID,
D.Name,
D.Status,
D.MonthsAgoSinceLastCheck,
PreviousStatus = N.Status
FROM
DataWithDates AS D
LEFT JOIN DataWithDates AS N ON
D.PersonID = N.PersonID AND
N.MonthDate = DATEADD(MONTH, -1 * D.MonthsAgoSinceLastCheck, D.MonthDate)
I'm assuming your MonthDate has values for all rows, otherwise the conversion will fail. I'm also assuming that your - values for MonthsAgoSinceLastCheck are actually NULL.
try this:
select *,LAG(Status) OVER(Partition by Name Order by MonthDate,Id) AS PreviousSatus
from tab1
order by id
SQl Fiddle:http://sqlfiddle.com/#!18/04407/4

How to rename column before unpivot? SQL Server

Help I want to rename a column before using the unpivot method
My table is like this:
List item
id| value| ENE| FEB| MAR| ABR| MAY
1 | dsads|2000|2334|2344|2344|2344
after unpivot I get something like this
id| value| month| amount
1 | dads| ENE | 2000
2 | sadf| FEB | 2334
but I want something like this
id| value| month| amount
1 | dads| 01 | 2000
2 | sadf| 02 | 2334
This is my query
select
[año], [Empresa], [Region], [Suc], [CC], [Cuenta],
[Subcuenta], [Descripcion], Periodo, Importe
from
(select * from [dbo].['P Cargado$']) S
unpivot
(Importe for Periodo in
([ENE], [FEB], [MAR], [ABR], [MAY], [JUN], [JUL], [AGO], [SEP],[OCT], [NOV], [DIC])
) AS unpvt;
If you use cross apply(values ..) to unpivot, you could do so like this:
select t.id, t.value, x.*
from t
cross apply (values (1,ene),(2,feb),(3,mar),(4,abr),(5,may)) x (Mnth,Amount)
rextester demo: http://rextester.com/SZDP50356
returns:
+----+-------+------+--------+
| id | value | Mnth | Amount |
+----+-------+------+--------+
| 1 | dsads | 1 | 2000 |
| 1 | dsads | 2 | 2334 |
| 1 | dsads | 3 | 2344 |
| 1 | dsads | 4 | 2344 |
| 1 | dsads | 5 | 2344 |
+----+-------+------+--------+

SQL Server selecting minimum value from duplicates

From the below table how can I pull the minimum value from CODE column for each duplicated USERID.
USER_ID | CODE | ROW_ID | NAME
1111111111 | -0.118 | 1 | USER1
1111111111 | 91.528 | 2 | USER2
2222222222 | 92.41 | 3 | USER3
2222222222 | 10.85 | 4 | USER4
2222222222 | 56.02 | 5 | USER5
3333333333 | -0.324 | 6 | USER6
3333333333 | 12.78 | 7 | USER7
4444444444 | 0.0002 | 8 | USER8
4444444444 | -1.324 | 9 | USER9
5555555555 | 93.598 | 10 | USER11
5555555555 | 101.35 | 11 | USER12
5555555555 | -5.425 | 12 | USER13
I tried the below query, but getting only the USER_ID and MIN(CODE). How to get the entire row as below said output?
SELECT USER_ID, min(CODE) minCODE
FROM TABLE1
GROUP BY USER_ID
The output should be:
USER_ID | CODE | ROW_ID | NAME
1111111111 | -0.118 | 1 | USER1
2222222222 | 10.85 | 4 | USER4
3333333333 | -0.324 | 6 | USER6
4444444444 | -1.324 | 9 | USER9
5555555555 | -5.425 | 12 | USER13
try this
;with a as (
SELECT
*
,ROW_NUMBER() OVER(PARTITION BY USER_ID ORDER BY CODE) r
FROM TABLE1
)
SELECT *
FROM a
WHERE r = 1
You are almost there, use result from your query to get another columns data
;WITH minvalue AS
(
SELECT USER_ID
, MIN(CODE) AS MinCode ¨
FROM TABLE1
GROUP BY USER_ID
)
SELECT t.USER_ID
, t.CODE
, t.ROW_ID
, t.NAME
FROM TABLE1 t
INNER JOIN minvalue mv ON mv.USER_ID = t.USER_ID
AND mv.MinCode = t.CODE
You'd need to run another pass with your initial query as a derived table:
select
USER_ID, CODE, ROW_ID, NAME
from
TABLE1
inner join
(SELECT USER_ID, min(CODE) minCODE
FROM TABLE1
GROUP BY USER_ID) derived TABLE1.USER_ID on derived.USER_ID
This way, you'll get your MIN, then you use that to grab the rest of the data from the table.

Resources