SQL: Update Column with increment numbers based on 2 Columns - sql

I have 2 Columns and i need to reorder one of them with an Update statement.
Here is an Example:
Date_time---------Priority
20.07.2018 10
21.07.2018 3
21.07.2018 13
21.07.2018 4
22.07.2018 23
23.07.2018 3
23.07.2018 7
And i need to get this:
Date_time---------Priority
20.07.2018 10
21.07.2018 10
21.07.2018 20
21.07.2018 30
22.07.2018 10
23.07.2018 10
23.07.2018 20
I need to change the Priority column based on current order and Date. The new order should be separated by 10... 10, 20, 30, 40, 50...
Can there someone help? Thanks.

you can try below query by using row_number funtion
update A
set Priority= 10*rn
from TableA A inner join
( select date_time, row_number() over(partition by Date_time order by Date_time ) as rn from TableA
) as B
on A.Date_time=B.Date_time

CREATE TABLE #Table1
([Date_time] varchar(10), [Priority] int)
;
INSERT INTO #Table1
([Date_time], [Priority])
VALUES
('20.07.2018', 10),
('21.07.2018', 3),
('21.07.2018', 13),
('21.07.2018', 4),
('22.07.2018', 23),
('23.07.2018', 3),
('23.07.2018', 7)
with cte as
(
SELECT *
,row_number() OVER (
PARTITION BY REPLACE([Date_time], '.', '-') ORDER BY [Priority]
) AS rn
FROM #Table1
) select [Date_time],(10*rn) [Priority] from cte
output
Date_time (No column name)
20.07.2018 10
21.07.2018 10
21.07.2018 20
21.07.2018 30
22.07.2018 10
23.07.2018 10
23.07.2018 20

replace [Table Name] with your table name
;with cte as
(
select DENSE_RANK() over(partition by date_time order by priority)*10 as newPriority,date_time,priority from [Table Name]
)
update [Table Name] set [priority]= newPriority from cte
where [Table Name].[priority]=cte.priority and [Table Name].Date_time=cte.Date_time

Related

Efficient way to cluster a timeline OR reconstruct a batch number

I'm working on a large dataset (150k / day) of a tester database. Each row contains data about a specific test of the product. Each tester inserts the results of his test.
I want to do some measurements like pass-fail-rate over a shift per product and tester. The problem is there are no batch numbers assigned so I can't select this easy.
Considering the given subselect of the whole table:
id tBegin orderId
------------------------------------
1 2018-10-20 00:00:05 1
2 2018-10-20 00:05:15 1
3 2018-10-20 01:00:05 1
10 2018-10-20 10:03:05 3
12 2018-10-20 11:04:05 8
20 2018-10-20 14:15:05 3
37 2018-10-20 18:12:05 1
My goal is it to cluster the data to the following
id tBegin orderId pCount
--------------------------------------------
1 2018-10-20 00:00:05 1 3
10 2018-10-20 10:03:05 3 1
12 2018-10-20 11:04:05 8 1
20 2018-10-20 14:15:05 3 1
37 2018-10-20 18:12:05 1 1
A simple GROUP BY orderID won't do the trick, so I came upwith the following
SELECT
MIN(c.id) AS id,
MIN(c.tBegin) AS tBegin,
c.orderId,
COUNT(*) AS pCount
FROM (
SELECT t2.id, t2.tBegin, t2.orderId,
( SELECT TOP 1 t.id
FROM history t
WHERE t.tBegin > t2.tBegin
AND t.orderID <> t2.orderID
AND <restrict date here further>
ORDER BY t.tBegin
) AS nextId
FROM history t2
) AS c
WHERE <restrict date here>
GROUP BY c.orderID, c.nextId
I left out the WHEREs that select the correct date and tester.
This works, but it seams very inefficient. I have worked with small databases, but I'm new to SQL Server 2017.
I appreciate your help very much!
You can use window functions for this:
DECLARE #t TABLE (id INT, tBegin DATETIME, orderId INT);
INSERT INTO #t VALUES
(1 , '2018-10-20 00:00:05', 1),
(2 , '2018-10-20 00:05:15', 1),
(3 , '2018-10-20 01:00:05', 1),
(10, '2018-10-20 10:03:05', 3),
(12, '2018-10-20 11:04:05', 8),
(20, '2018-10-20 14:15:05', 3),
(37, '2018-10-20 18:12:05', 1);
WITH cte1 AS (
SELECT *, CASE WHEN orderId = LAG(orderId) OVER (ORDER BY tBegin) THEN 0 ELSE 1 END AS chg
FROM #t
), cte2 AS (
SELECT *, SUM(chg) OVER(ORDER BY tBegin) AS grp
FROM cte1
), cte3 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY grp ORDER BY tBegin) AS rn
FROM cte2
)
SELECT *
FROM cte3
WHERE rn = 1
The first cte assigns a "change flag" to each row where the value changed
The second cte uses a running sum to convert 1s and 0s to a number which can be used to group rows
Finally you number rows within each group and select first row per group
Demo on DB Fiddle
You can use cumulative approach :
select min(id) as id, max(tBegin), orderid, count(*)
from (select h.*,
row_number() over (order by id) as seq1,
row_number() over (partition by orderid order by id) as seq2
from history h
) h
group by orderid, (seq1 - seq2)
order by id;

How to get the second largest Time for a certain Foreign Key in SQL Server

For example if I have
I want to return the second largest time for each "FkID".
In this case it have to return the values with ID = 1, 2, 3, 4, 20, 23, 26,
with the associated time.
Here is some sample data:
ID FkID Time
1 1 13:22.9
2 2 14:44.8
3 3 15:43.4
4 4 16:31.0
19 11 06:20.6
20 11 06:28.6
21 11 06:36.3
22 12 02:34.9
23 12 02:39.5
24 12 02:44.3
25 13 22:47.2
26 13 22:56.3
27 13 23:01.8
28 14 13:03.3
Try this :
select max(date)
from table_name t1
where date < (select max(date) from table_name where FkID= t1.FkID)
group by FkID
Give ROW_NUMBER with CTE.
Query
;with cte as
(
select rn = row_number() over
(
partition by fkid
order by [Date] desc
), *
from your_table_name
)
select fkid, min([Date]) as [Date]
from cte
where rn < 3
group by fkid;

SQL: first row of group by after join and order

Assuming we got two below tables:
TravelTimes
OriginId DestinationId TotalJourneyTime
1 1 10
1 2 20
2 2 30
2 3 40
1 3 50
Destinations
DestinationId Name
1 Destination 1
2 Destination 2
3 Destination 3
How do I find the quickest journey between each origin and destination?
I want to join TravelTimes with Destinations by DestinationId and then group them by OriginId and sort each group by TotalJourneyTime and select the first row of each group.
I did try joining and grouping, but it seems group by is not the solution for my case as I don't have any aggregation column in the output.
Expected output
OriginId DestinationId DestinationName TotalJourneyTime
1 1 Destination 1 10
2 3 Destination 3 40
Use a RANK to rank each journey partitioned by the origin and destination and ordered by the travel time
WITH RankedTravelTimes
AS
(
select originid,
destinationId,
totaljourneytime,
rank() over (partition by originid,destinationid order by totaljourneytime ) as r
from traveltimes
)
SELECT rtt.*, d.name
FROM RankedTravelTimes rtt
INNER JOIN Destinations d
ON rtt.destinationId = d.id
WHERE rtt.r=1
The above will include both the journey from 1-2 and 2-2 as separate. If you're only interested in the destination you can remove originId out of the partition.
Not sure I see the problem here with just joining and grouping the data with a MIN on the journey time:
CREATE TABLE #Traveltimes
(
[OriginId] INT ,
[DestinationId] INT ,
[TotalJourneyTime] INT
);
INSERT INTO #Traveltimes
( [OriginId], [DestinationId], [TotalJourneyTime] )
VALUES ( 1, 1, 10 ),
( 1, 2, 20 ),
( 2, 2, 30 ),
( 2, 3, 40 ),
( 2, 3, 50 );
CREATE TABLE #Destinations
(
[DestinationId] INT ,
[Name] VARCHAR(13)
);
INSERT INTO #Destinations
( [DestinationId], [Name] )
VALUES ( 1, 'Destination 1' ),
( 2, 'Destination 2' ),
( 3, 'Destination 3' );
SELECT d.DestinationId ,
d.Name ,
tt.OriginId ,
MIN(tt.TotalJourneyTime) MinTime
FROM #Destinations d
INNER JOIN #Traveltimes tt ON tt.DestinationId = d.DestinationId
GROUP BY tt.OriginId ,
d.DestinationId ,
d.Name
DROP TABLE #Destinations
DROP TABLE #Traveltimes
Gives you:
DestinationId Name OriginId MinTime
1 Destination 1 1 10
2 Destination 2 1 20
2 Destination 2 2 30
3 Destination 3 2 40
Note: why do you travel from destination 1 to itself?
I think you want the following:
;with cte as(select *, row_number() over(partition by DestinationId order by TotalJourneyTime) rn
from TravelTimes)
select * from cte c
join Destinations d on c.DestinationId = d.DestinationId
where c.rn = 1

Get Starting and ending of a Dataset in SQL Server 2008

I need to extract the starting and ending points of a data set from a table. For Ex if data is like:
1
5
10
15
20
40
45
50
55
60
65
70
Now the 2 data sets are 1 - 20 and 40 - 70. So the Data will always be sequential and the difference between points in a single dataset will max be 7. So the resultant query should give me 3 columns:
1. 5 15
2. 45 65
i.e second and second last point in the dataset.
Is it possible to do without using a cursor of forloop. Please post a query if you can.
I tried doing is using over and partition by but no luck
If I understand you properly, this returns what you're asking for.
DECLARE #tmp TABLE
(
numVal INT PRIMARY KEY
);
INSERT #tmp
VALUES
( 1 )
,( 5 )
,( 10 )
,( 15 )
,( 20 )
,( 40 )
,( 45 )
,( 50 )
,( 55 )
,( 60 )
,( 65 )
,( 70 );
;WITH breaks AS
(
SELECT
t.numval breakMax
, ROW_NUMBER()
OVER(
ORDER BY t.numval
) breakGroup
FROM
#tmp t
WHERE
NOT EXISTS
(
SELECT
NULL
FROM
#tmp t1
WHERE
t1.numVal > t.numVal
AND
t1.numVal <= t.numVal + 7
)
)
SELECT
v.breakGroup
, MIN(v.numval) secondNum
, MAX(v.numVal) secondLastNum
FROM
(
SELECT
t.numVal
, br.breakGroup
, ROW_NUMBER()
OVER(
PARTITION BY
br.breakGroup
ORDER BY
t.numval
) ar
, ROW_NUMBER()
OVER(
PARTITION BY
br.breakGroup
ORDER BY
t.numval DESC
) dr
FROM
#tmp t
CROSS APPLY
(
SELECT
TOP 1
breakGroup
FROM
breaks b
WHERE
b.breakMax >= t.numVal
ORDER BY
b.breakGroup
) br
) v
WHERE
v.ar = 2
OR
v.dr = 2
GROUP BY
v.breakGroup

TSQL Sweepstakes Script

I need to run a sweepstakes script to get X amount of winners from a customers table. Each customer has N participations. The table looks like this
CUSTOMER-A 5
CUSTOMER-B 8
CUSTOMER-C 1
I can always script to have CUSTOMER-A,B and C inserted 5, 8 and 1 times respectively in a temp table and then select randomly using order by newid() but would like to know if there's a more elegant way to address this.
(Update: Added final query.)
(Update2: Added single query to avoid temp table.)
Here's the hard part using a recursive CTE plus the final query that shows "place".
Code
DECLARE #cust TABLE (
CustomerID int IDENTITY,
ParticipationCt int
)
DECLARE #list TABLE (
CustomerID int,
RowNumber int
)
INSERT INTO #cust (ParticipationCt) VALUES (5)
INSERT INTO #cust (ParticipationCt) VALUES (8)
INSERT INTO #cust (ParticipationCt) VALUES (1)
INSERT INTO #cust (ParticipationCt) VALUES (3)
INSERT INTO #cust (ParticipationCt) VALUES (4)
SELECT * FROM #cust
;WITH t AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM t
WHERE lvl < ParticipationCt
)
INSERT INTO #list (CustomerID, RowNumber)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID())
FROM t
--<< All rows
SELECT * FROM #list ORDER BY RowNumber
--<< All customers by "place"
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM #list
GROUP BY CustomerID
Results
CustomerID ParticipationCt
----------- ---------------
1 5
2 8
3 1
4 3
5 4
CustomerID RowNumber
----------- -----------
4 1
1 2
1 3
2 4
1 5
5 6
2 7
2 8
4 9
2 10
2 11
2 12
1 13
5 14
5 15
3 16
5 17
1 18
2 19
2 20
4 21
CustomerID Place
----------- -----
4 1
1 2
2 3
5 4
3 5
Single Query with No Temp Table
It is possible to get the answer with a single query that does not use a temp table. This works fine, but I personally like the temp table version better so you can validate the interim results.
Code (Single Query)
;WITH List AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM List
WHERE lvl < ParticipationCt
),
RandomOrder AS (
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID()) AS RowNumber
FROM List
)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM RandomOrder
GROUP BY CustomerID
try this:
Select Top X CustomerId
From (Select CustomerId,
Rand(CustomerId) *
Count(*) /
(Select Count(*)
From Table) Sort
From Table
Group By CustomerId) Z
Order By Sort Desc
EDIT: abovbe assumed multiple rows per customer, one row per participation... Sorry, following assumes one row per customer, with column Participations holding number of participations for that customer.
Select Top 23 CustomerId
From ( Select CustomerId,
Participations - RAND(CustomerId) *
(Select SUM(Participations ) From customers) sort
from customers) Z
Order By sort desc