This is the result of my first sql statement:
SELECT
count(*) countQuarter, Hour, Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter
which gives the following results:
Count Hour Quarter Rownum
497 0 15 1
842 0 30 2
1033 0 45 3
1120 0 60 4
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
I desire a result, where the column fullCount is the sum of the Count of the actual row and the next 3!
Count Hour Quarter Rownum Fullcount
497 0 15 1 3492
842 0 30 2 4230
1033 0 45 3 4655
1120 0 60 4 ...
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
How can this be done with grouping or analytical functions in SQL Server?
For SQL Server 2012, yes this can be done:
declare #t table ([Count] int,[Hour] int,[Quarter] int,Rownum int)
insert into #t([Count],[Hour],[Quarter],Rownum) values
(497 , 0 , 15 , 1 ),
(842 , 0 , 30 , 2 ),
(1033 , 0 , 45 , 3 ),
(1120 , 0 , 60 , 4 ),
(1235 , 1 , 15 , 5 ),
(1267 , 1 , 30 , 6 ),
(1267 , 1 , 45 , 7 ),
(1267 , 1 , 60 , 8 ),
(1267 , 2 , 15 , 9 ),
(1267 , 2 , 30 , 10 )
select *,SUM([Count]) OVER (
ORDER BY rownum
ROWS BETWEEN CURRENT ROW AND
3 FOLLOWING)
from #t
Here I'm using #t as your current result set - you may be able to adapt this into your current query or may have to place your current query in a CTE.
Unfortunately, the ROWS BETWEEN syntax is only valid on 2012 and later.
Tested the logical scenario and it works, but I don't have your data, so in your case it should look roughly like this:
;WITH CTE as (SELECT count(*) countQuarter,Hour,Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter)
SELECT *, CA.Fullcount
FROM CTE
CROSS APPLY (SELECT SUM(countQuarter) Fullcount FROM CTE C WHERE C.ID BETWEEN CTE.ID AND CTE.ID+3) CA
Related
WITH CTE1
AS (SELECT 'ABC' AS [Name],
4 AS [Call Count],
0 AS [Time_Slot]
UNION
SELECT 'XYX' AS [Name],
7 AS [Call Count],
1 AS [Time_Slot]
UNION
SELECT 'TRT' AS [Name],
6 AS [Call Count],
6 AS [Time_Slot]
UNION
SELECT 'DCFG' AS [Name],
8 AS [Call Count],
7 AS [Time_Slot]
UNION
SELECT 'DCS' AS [Name],
45 AS [Call Count],
18 AS [Time_Slot]
UNION
SELECT 'XYX' AS [Name],
45 AS [Call Count],
9 AS [Time_Slot]
)
SELECT *
FROM CTE1;
consider the output of the above code is
Name Call Count Time_Slot
ABC 4 0
DCFG 50 7
DCS 45 18
TRT 6 6
XYX 7 1
XYX 45 9
I wanted to output per user 24 hours data like below,
This data is for user DCFG likewise I wanted for each user (ABC, DCS, TRT, XYX)
Name Call Count Time_Slot
DCFG 0 0
DCFG 0 1
DCFG 0 2
DCFG 0 3
DCFG 0 4
DCFG 0 5
DCFG 0 6
DCFG 50 7
DCFG 0 8
DCFG 0 9
DCFG 0 10
DCFG 0 11
DCFG 0 12
DCFG 0 13
DCFG 0 14
DCFG 0 15
DCFG 0 16
DCFG 0 17
DCFG 0 18
DCFG 0 19
DCFG 0 20
DCFG 0 21
DCFG 0 22
DCFG 0 23
Now, what I have tried
it makes sense by using joins I'll not able to achieve what I wanted, by using cross join
I get all Time slots but its repeated 24-time slots entry for each user rows, for example
user 'XYX' CTE1 has Two Entries As Below
XYX 7 1
XYX 45 9
Cross Join create 24-time slot entry for above each row.
Can anyone suggest to me how I can achieve this, Thank you In advance
Use a (inline) tally, CROSS JOIN it to your user table (I assume you have one), and then LEFT JOIN that to your dataset above:
WITH Tally AS(
SELECT I
FROM(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23))V(I)),
UserTimes AS(
SELECT U.[Name],
T.I AS TimeSlot
FROM dbo.Users U
CROSS JOIN T)
SELECT YT.[Name],
C.CallCount,
YT.TimeSlot
FROM UserTimes UT
LEFT JOIN CTE1 C ON UT.[Name] = C.[Name]
AND UT.TimeSlot = C.TimeSlot;
I need to be able to treat each row as if it was an 'incoming' row into the table and compare it to previous transactions, and when certain conditions are met, output only that row.
For example, I want to output the most recent row if it has 3 or more occurrences on the same accounts, that the current row is occurring within 30 minutes of the previous row, and all rows previous rows and the current row has a total amount of 400 or more.
CREATE TABLE #table
(
tran_date DATETIME,
acct INT,
amt INT
)
INSERT INTO #table
VALUES ('2019-07-01 01:21:08', 1, 100), ('2019-07-01 01:30:50', 1, 200),
('2019-07-01 01:46:21', 1, 150), ('2019-07-01 03:23:41', 1, 50),
('2019-07-01 03:24:40', 1, 300), ('2019-07-01 09:53:28', 2, 400),
('2019-07-01 12:56:15', 2, 50), ('2019-07-01 17:43:55', 2, 500),
('2019-07-01 05:15:54', 3, 20), ('2019-07-01 05:30:00', 3, 50),
('2019-07-01 05:36:27', 3, 10), ('2019-07-01 05:59:00', 3, 250),
('2019-07-01 06:18:00', 3, 80), ('2019-07-01 06:25:56', 3, 100),
('2019-07-01 09:34:34', 4, 150), ('2019-07-01 09:47:24', 4, 300),
('2019-07-01 09:52:25', 4, 50), ('2019-07-01 11:34:34', 4, 250),
('2019-07-01 11:47:24', 4, 100), ('2019-07-01 11:52:25', 4, 150)
My goal is to try and get a way that can do this in SQL using the exists operator, if it can also be done without creating several temp table and joining them back together I would be thankful for that. I'm trying to avoid having to do several joins for certain reasons.
select *
from #table a
where exists (select 1
from #table b
where a.acct = b.acct
and b.tran_date >= dateadd(minute, -30, a.tran_date)
and b.tran_date < a.tran_date
having sum(amt) >= 400 and count(*) >= 3)
I expect the query to output these rows if i did my math right:
tran_date acct amt
------------------------------
2019-07-01 01:46:21 1 150
2019-07-01 06:18:00 3 80
2019-07-01 06:25:56 3 100
2019-07-01 09:52:25 4 50
2019-07-01 11:52:25 4 150
So the first row here is picked up because it is the 3rd transaction, all previous are within 30 minutes of the row before it, and this is the transaction that reaches $400 or more of the total amount for the 3 previous.
My current query returns no output, and I think it is something to do with the having clause. I could be wrong but help with this would be much appreciated.
EDIT:
In the end I would need the row numbers or rankings for each row to look like this:
tran_date acct amt RN Time_Diff Running_Total
2019-07-01 01:21:08.000 1 100 1 NULL 100
2019-07-01 01:30:50.000 1 200 2 9 300
2019-07-01 01:46:21.000 1 150 3 16 450
2019-07-01 03:23:41.000 1 50 1 97 50
2019-07-01 03:24:40.000 1 300 2 1 350
2019-07-01 09:53:28.000 2 400 1 NULL 400
2019-07-01 12:56:15.000 2 50 1 183 50
2019-07-01 17:43:55.000 2 500 1 287 500
2019-07-01 05:15:54.000 3 20 1 NULL 20
2019-07-01 05:30:00.000 3 50 2 15 70
2019-07-01 05:36:27.000 3 10 3 6 80
2019-07-01 05:59:00.000 3 250 4 23 330
2019-07-01 06:18:00.000 3 80 5 19 410
2019-07-01 06:25:56.000 3 100 6 7 510
2019-07-01 09:34:34.000 4 150 1 NULL 150
2019-07-01 09:47:24.000 4 300 2 13 450
2019-07-01 09:52:25.000 4 50 3 5 500
2019-07-01 11:34:34.000 4 250 1 102 250
2019-07-01 11:47:24.000 4 100 2 13 350
2019-07-01 11:52:25.000 4 150 3 5 550
The goal is that once the rows are no longer <= 30 minutes apart from each other or the acct # changes, the row number or rank restarts, along with restarting the running total of the amount. If I have it in a format like this then I can query for the results rows I want.
The issue with your example is that HAVING COUNT() does not work the way you expect.
Consider the following query
SELECT
tran_date, acct, amt,
ROW_NUMBER() OVER (PARTITION BY acct ORDER BY tran_date) AS RN
FROM #table
This will give you the number of each row per account
Then
SELECT *
FROM (
SELECT
tran_date, acct, amt,
ROW_NUMBER() OVER (PARTITION BY acct ORDER BY tran_date) AS RN
FROM #table
) X
WHERE RN >= 3
will give you all the row with 3 or more.
Oh, you want the last one? You can do it like this:
SELECT *
FROM (
SELECT
tran_date, acct, amt,c
ROW_NUMBER() OVER (PARTITION BY acct ORDER BY tran_date ASC) AS RN,
ROW_NUMBER() OVER (PARTITION BY acct ORDER BY tran_date DESC) AS RN_REV
FROM #table
) X
WHERE RN >= 3 AND RN_REV = 1
Without temp tables here's an option which would require a couple sub-queries.
You'll need LAG and DATEDIFF to get the number of minutes between tran_date and the previous row.
Then basically a running total on the evaluation of whether or not the time difference is greater than 30. So you know when to reset your numbering.
Then you can use ROW_NUMBER() and SUM() partitioning it by the acct and your "reset" indicator.
Here's a working example:
DECLARE #TestData TABLE
(
[tran_date] DATETIME
, [acct] INT
, [amt] INT
);
INSERT INTO #TestData
VALUES ( '2019-07-01 01:21:08', 1, 100 )
, ( '2019-07-01 01:30:50', 1, 200 )
, ( '2019-07-01 01:46:21', 1, 150 )
, ( '2019-07-01 03:23:41', 1, 50 )
, ( '2019-07-01 03:24:40', 1, 300 )
, ( '2019-07-01 09:53:28', 2, 400 )
, ( '2019-07-01 12:56:15', 2, 50 )
, ( '2019-07-01 17:43:55', 2, 500 )
, ( '2019-07-01 05:15:54', 3, 20 )
, ( '2019-07-01 05:30:00', 3, 50 )
, ( '2019-07-01 05:36:27', 3, 10 )
, ( '2019-07-01 05:59:00', 3, 250 )
, ( '2019-07-01 06:18:00', 3, 80 )
, ( '2019-07-01 06:25:56', 3, 100 )
, ( '2019-07-01 09:34:34', 4, 150 )
, ( '2019-07-01 09:47:24', 4, 300 )
, ( '2019-07-01 09:52:25', 4, 50 )
, ( '2019-07-01 11:34:34', 4, 250 )
, ( '2019-07-01 11:47:24', 4, 100 )
, ( '2019-07-01 11:52:25', 4, 150 );
--Read comments from inner most sub query out
SELECT [b].[tran_date]
, [b].[acct]
, [b].[amt]
, ROW_NUMBER() OVER ( PARTITION BY [b].[acct], [b].[diffincrement] ORDER BY [b].[tran_date]) AS [RN] --Third: We can now partition on our acct and "reset" indicator(diffincrement) to get our row number.
, [b].[Time_Diff]
, SUM([b].[amt]) OVER ( PARTITION BY [b].[acct], [b].[diffincrement] ORDER BY [b].[tran_date]) AS [Running_Total] --Third: We can now partition on our acct and "reset" indicator(diffincrement) to get our running total.
FROM ( --Second: Here we now evalute Time_Diff and sum to basically give a running total so we know when to reset based on that.
SELECT *
, SUM(CASE WHEN [a].[Time_Diff] >= 30 THEN 1 ELSE 0 END
) OVER ( PARTITION BY [a].[acct] ORDER BY [a].[tran_date]) AS [diffincrement]
FROM (
--First: Here we use LAG and datediff to find the different in minutes of the previous row.
SELECT *
, DATEDIFF(MINUTE, LAG([tran_date], 1, [tran_date]) OVER ( PARTITION BY [acct] ORDER BY [tran_date]), [tran_date]) AS [Time_Diff]
FROM #TestData
) AS [a]
) AS [b]
ORDER BY [b].[acct]
, [b].[tran_date];
Giving you the final result of:
tran_date acct amt RN Time_Diff Running_Total
----------------------- ----------- ----------- -------------------- ----------- -------------
2019-07-01 01:21:08.000 1 100 1 0 100
2019-07-01 01:30:50.000 1 200 2 9 300
2019-07-01 01:46:21.000 1 150 3 16 450
2019-07-01 03:23:41.000 1 50 1 97 50
2019-07-01 03:24:40.000 1 300 2 1 350
2019-07-01 09:53:28.000 2 400 1 0 400
2019-07-01 12:56:15.000 2 50 1 183 50
2019-07-01 17:43:55.000 2 500 1 287 500
2019-07-01 05:15:54.000 3 20 1 0 20
2019-07-01 05:30:00.000 3 50 2 15 70
2019-07-01 05:36:27.000 3 10 3 6 80
2019-07-01 05:59:00.000 3 250 4 23 330
2019-07-01 06:18:00.000 3 80 5 19 410
2019-07-01 06:25:56.000 3 100 6 7 510
2019-07-01 09:34:34.000 4 150 1 0 150
2019-07-01 09:47:24.000 4 300 2 13 450
2019-07-01 09:52:25.000 4 50 3 5 500
2019-07-01 11:34:34.000 4 250 1 102 250
2019-07-01 11:47:24.000 4 100 2 13 350
2019-07-01 11:52:25.000 4 150 3 5 500
We have a table [Kpis] that looks like the following:
RawId EmpId Date Hour Min KpiValue KpiName
106 ABC123 20160310 8 0 3 Kpi1
124 ABC123 20160310 8 0 65 Kpi1
121 ABC123 20160310 8 15 12 Kpi2
109 ABC109 20160310 8 0 34 Kpi2
112 ABC908 20160310 9 5 3 Kpi1
118 ABC907 20160310 8 30 24 Kpi1
115 ABC123 20160310 8 15 54 Kpi1
I would like to group by EmpId, KpiName, Date, Hour. So, for example, with this data, Kpi1 for EmpId ABC123 at Hour 8 would be 122.
So I tried using the CASE statement, but the result is incorrect. I haven't checked the actual totals in the result, but the sums should be correct. It's the format of the result that's incorrect; every empid has two rows: one for Kpi1 and one for Kpi2.
select empid,
case kpiname when 'Kpi1' then sum(kpivalue) end as 'Kpi1',
case kpiname when 'Kpi2' then sum(kpivalue) end as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
How can I use the Case statement to fix the results?
Thanks.
Put the case inside your sum, such that you for each KpiName only sums the relevant values.
SELECT
EmpId,
[Hour],
SUM(
CASE
WHEN KpiName = 'Kpi1' THEN KpiValue
ELSE 0
END
) Kpi1,
SUM(
CASE
WHEN KpiName = 'Kpi2' THEN KpiValue
ELSE 0
END
) Kpi2
FROM
Kpis
GROUP BY
EmpId,
[Hour]
This produces this output
EmpId Hour Kpi1 Kpi2
ABC109 8 0 34
ABC123 8 122 12
ABC907 8 24 0
ABC908 9 3 0
SUM fucntion have to be outside of CASE:
select empid,
sum(case kpiname when 'Kpi1' then kpivalue end) as 'Kpi1',
sum(case kpiname when 'Kpi2' then kpivalue end) as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
You can also do this with the PIVOT functionality, which I believe is what you're actually trying to accomplish.
SELECT
*
FROM (
SELECT
EmpId,
KpiName,
[Hour],
KpiValue
FROM
Kpis
) SourceTable
PIVOT (
SUM(KpiValue)
FOR KpiName
IN ([Kpi1],[Kpi2])
) PivotTable
Which gives this output. Note the NULLs as opposed to the zeros, correctly showing the lack of data.
EmpId Hour Kpi1 Kpi2
ABC109 8 NULL 34
ABC123 8 122 12
ABC907 8 24 NULL
ABC908 9 3 NULL
I have a table in SQL Server with two fields.
Total Group
35645 24
12400 55
30000 41
I want to split each group into smaller segments of fixed size 7000, with the remainder of each group into the last segment. So, the output should look like below.
Segment Total Group
1 7000 24
2 7000 24
3 7000 24
4 7000 24
5 7000 24
6 645 24
1 7000 55
2 5400 55
1 7000 41
2 7000 41
3 7000 41
4 7000 41
5 2000 41
This should do it:
declare #t table (Total int,[Group] int)
insert into #t(Total,[Group]) values
(35645,24 ),
(12400,55 ),
(30000,41 )
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY number)-1 n
from master..spt_values
)
select
n.n+1 as Segment,
CASE WHEN (n.n+1)*7000 < t.Total THEN 7000
ELSE t.Total - (n.n*7000) END as Total,
t.[Group]
from
#t t inner join
Numbers n on n.n*7000 < t.Total
(If you already have a Numbers table you can eliminate that part. I'm using spt_values just as a table that I know has plenty of rows in it, so that the ROW_NUMBER() expression should generate all of the necessary numbers)
Results:
Segment Total Group
-------------------- -------------------- -----------
1 7000 24
2 7000 24
3 7000 24
4 7000 24
5 7000 24
6 645 24
1 7000 55
2 5400 55
1 7000 41
2 7000 41
3 7000 41
4 7000 41
5 2000 41
I prepared following SELECT statement using SQL CTE expression and SQL numbers table function
declare #divisor int = 7000
;with CTE as (
select
Total,
[Group],
#divisor divisor,
(Total / #divisor) quotient,
(Total % #divisor) reminder
from t
), NT as (
SELECT i FROM dbo.NumbersTable(1, (select max(quotient) from CTE) ,1)
)
select
case when i = 0 then reminder else divisor end as Total,
[Group]
from (
select *
from CTE, NT
where quotient >= i
union all
select *, 0 as i
from CTE
where reminder >= 0
) t
order by [Group], i desc
Goal : I want to find the lowest NR of DayInStock where the Cummsold > Balance
The query is as follow
SELECT TOP (100) PERCENT
a.MonthNR
, a.DayNR
, a.DayInStock
, a.CummSold
, a.WarehouseID
, a.ItemID
, a.[Group]
, a.Balance
, a.CountryNumber
, a.Country
FROM dbo.VW_Critical_01_01 AS a
JOIN (SELECT MIN(DayInStock) AS DayInStock
, MIN(CummSold) AS Cummsold
, Balance
, ItemID
, [Group]
FROM dbo.VW_Critical_01_01
WHERE CummSold > Balance
GROUP BY DayInStock, CummSold, Balance, ItemID, [Group]
) AS b
ON b.DayInStock = a.DayInStock
AND b.ItemID = a.ItemID
AND b.[Group] = a.[Group]
ORDER BY a.ItemID, a.DayInStock
The query gives my as result:
MonthNR DayNR DayInStock CummSold ItemID Group Balance CountryNumber Country
**2 4 11 2902.492233 100049V3 1 2894 370 Sweden
2 4 11 2902.492233 100049V3 1 2894 280 Norway
2 4 11 2902.492233 100049V3 1 2894 270 Portugal
2 4 11 2902.492233 100049V3 1 2894 460 Finland
2 4 11 2902.492233 100049V3 1 2894 110 Switzerland**
2 5 12 2982.376102 100049V3 1 2894 370 Sweden
2 5 12 2982.376102 100049V3 1 2894 280 Norway
2 5 12 2982.376102 100049V3 1 2894 270 Portugal
2 5 12 2982.376102 100049V3 1 2894 460 Finland
2 5 12 2982.376102 100049V3 1 2894 110 Switzerland
Where I only want to see the values marked as BOLD
As for those the CummSold > Balance (2902 is larger then 2894) and the daynumber is the lowest 11 instead of 12
My SQL Server knowledge seems to stop here. I searched and tried numerous things and perhaps i'm just overlooking something very "stupid"
Any help or suggestions would be appreciated
For SQL Server 2005+ you can try using analytical functions:
;WITH CTE AS
(
SELECT a.MonthNR,
a.DayNR,
a.DayInStock,
a.CummSold,
a.WarehouseID,
a.ItemID,
a.[Group],
a.Balance,
a.CountryNumber,
a.Country,
ROW_NUMBER() OVER(PARTITION BY ItemID, [Group], Country
ORDER BY DayInStock) RN
FROM dbo.VW_Critical_01_01 a
WHERE CummSold > Balance
)
SELECT *
FROM CTE
WHERE RN = 1
something like this?
select DayNR
, DayInStock
, CummSold
, WarehouseID
, ItemID
, [Group]
, Balance
, CountryNumber
, Country
from(SELECT DayInStock
, DayNR
, DayInStock
, CummSold
, WarehouseID
, ItemID
, [Group]
, Balance
, CountryNumber
, Country
, min(DayInStock) over (partition by itemID,Group,Country) as minDayInStock
FROM dbo.VW_Critical_01_01
WHERE CummSold > Balance
)V
where V.DayInStock = V.minDayInStock