I'm trying to create a query that will return Total Claims reported in 0-3 days, 4-7 days, 8-14 days, and 15+
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
order by DATEDiff(DD,LossDate,DateReported);
This is what i get
TimeToReport NumofClaims
0 5
1 3
2 1
3 4
4 3
5 2
6 2
7 2
8 1
12 1
13 1
14 2
15 2
48 1
52 1
107 1
121 1
147 1
533 1
Basically i want to see the total for 0-3, 4-7,8-14,and the rest,,,, timeToReport
You can try to use SUM with CASW WHEN
select
SUM(CASW WHEN TimeToReport <= 3 THEN NumofClaims ELSE 0 END) '0~3 day',
SUM(CASW WHEN TimeToReport >= 4 AND TimeToReport <=7 THEN NumofClaims END) '4-7 days',
SUM(CASW WHEN TimeToReport >= 8 AND TimeToReport <=14 THEN NumofClaims ELSE 0 END) '8-14 days',
SUM(CASW WHEN TimeToReport >= 15 THEN NumofClaims ELSE 0 END) '15+ day'
from (
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num
from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
) t
The most simple way is going to be by creating your own temp table which includes the min and max for each bucket and then joining to it.
declare #t table (OrderedID int, EmpID int, EffDate date, Salary money)
insert into #t
values
(1,1234,'20150101',1)
,(2,1234,'20160101',2)
,(3,1234,'20170101',8)
,(4,1234,'20180101',15)
,(1,2351,'20150101',17)
,(5,1234,'20190101',4)
,(5,1234,'20190101',2)
,(5,1234,'20190101',9)
declare #Bin table (MinVal int, MaxVal int)
insert into #Bin
values
(1,3)
,(4,6)
,(7,9)
,(10,15)
,(15,20)
,(20,25)
Select
B.MinVal,count(T.EmpID) as EmpsInBin
From #t T
inner join #Bin B on T.Salary between B.MinVal and B.MaxVal
group by B.MinVal
Output
MinVal EmpsInBin
1 3
4 1
7 2
10 1
15 2
Related
I've this sp, which return a list of data, for each "month" (i.e. each row is a month). Somethings like that:
SELECT
*,
(CAST(t1.NumActivities AS DECIMAL) / t1.NumVisits) * 100 AS PercAccepted,
(CAST(t1.Accepted AS DECIMAL) / t1.Estimated) * 100 AS PercValue
FROM
(SELECT
MONTH(DateVisit) AS Month,
COUNT(*) AS NumVisits,
SUM(CASE WHEN DateActivity is not null THEN 1 ELSE 0 END) AS NumActivities,
SUM(Estimate) AS Estimated,
SUM(CASE WHEN DateActivity is not null THEN Estimate ELSE 0 END) AS Accepted
FROM [dbo].[Activities]
WHERE
DateVisit IS NOT NULL
AND (#year IS NULL OR YEAR(DateVisit) = #year)
AND (#clinicID IS NULL OR ClinicID = #clinicID)
GROUP BY MONTH(DateVisit)) t1
This is a result:
Month NumVisits NumActivities Estimated Accepted PercAccepted PercValue
1 5 1 13770.00 2520.00 20.00000000000 18.30065359477124
2 2 2 7900.00 7900.00 100.00000000000 100.00000000000000
3 1 0 2730.00 0.00 0.00000000000 0.00000000000000
8 1 1 3000.00 3000.00 100.00000000000 100.00000000000000
But as you can see, I could "miss" some Month (for example, here April "4" is missed).
Is it possible to insert, for the missing month/row, an empty (0) record? Such as:
Month NumVisits NumActivities Estimated Accepted PercAccepted PercValue
1 5 1 13770.00 2520.00 20.00000000000 18.30065359477124
2 2 2 7900.00 7900.00 100.00000000000 100.00000000000000
3 1 0 2730.00 0.00 0.00000000000 0.00000000000000
4 0 0 0 0 0 0
...
Here is a example with sample data:
CREATE TABLE #Report
(
Id INT,
Name nvarchar(max),
Percentage float
)
INSERT INTO #Report VALUES (1,'ONE',2.01)
INSERT INTO #Report VALUES (2,'TWO',3.01)
INSERT INTO #Report VALUES (5,'Five',5.01)
;WITH months(Month) AS
(
SELECT 1
UNION ALL
SELECT Month+1
FROM months
WHERE Month < 12
)
SELECT *
INTO #AllMonthsNumber
from months;
Your select query:
The left join will gives you the NULL for other months so just use ISNULL('ColumnName','String_to_replace')
\/\/\/\/
SELECT Month, ISNULL(Name,0), ISNULL(Percentage,0)
FROM AllMonthsNumber A
LEFT JOIN #Report B
ON A.Month = B.Id
EDIT:
Yes you can do it without creating AllMonthNumber Table:
You can use master..spt_values (found here) system table which contains the numbers so just with some where condition.
SELECT Number as Month, ISNULL(B.Name,0), ISNULL(Percentage,0)
FROM master..spt_values A
LEFT JOIN #Report B ON A.Number = B.Id
WHERE Type = 'P' AND number BETWEEN 1 AND 12
I have a following table called lobby
QueueID FkBranch IsActive Status AddedLocalTime CompletedTime FkAssistTypeID
553279 16 1 5 7/12/2019 20:06 7/12/2019 21:10 2
553278 16 1 5 7/12/2019 20:07 7/12/2019 21:11 1
553277 16 1 5 7/12/2019 20:08 7/12/2019 21:10 1
553276 16 1 5 7/12/2019 20:09 7/12/2019 21:11 1
553275 16 1 5 7/13/2019 20:10 7/13/2019 21:10 2
553274 16 1 5 7/13/2019 20:11 7/13/2019 21:11 2
553278 17 1 5 7/14/2019 20:07 7/14/2019 21:11 1
553277 17 1 5 7/14/2019 20:08 7/14/2019 21:10 1
553276 18 1 5 7/14/2019 20:09 7/14/2019 21:11 2
553275 18 1 5 7/15/2019 20:10 7/15/2019 21:10 2
553274 18 1 5 7/15/2019 20:11 7/15/2019 21:11 2
And Branch table and Its data as follows
BranchID BranchName IsActive
16 Delhi 1
17 Karnataka 1
18 Telangana 1
Now I need to get a count of FkAssistTypeID of each location between AddedLocalTime and also need to take summation of the time difference of AddedLocalTime and CompletedTime.
I have a function to get the time Difference of two dates and it as follows
dbo.fnTimetoSeconds(AddedLocalTime, CompletedTime, NULL)
CREATE FUNCTION [dbo].[fnTimetoSeconds]
(
#dateOne DATETIME,#dateTwo DATETIME,#dateToConvert DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE #date DATETIME
DECLARE #retValue INT
IF(#dateToConvert IS NULL)
BEGIN
SET #dateToConvert = CASE WHEN(#dateTwo>#dateOne) THEN #dateTwo-#dateOne ELSE #dateOne-#dateTwo END
END
SET #date = DATEADD(D, 0, DATEDIFF(D, 0, #dateToConvert))
IF(DATEPART(yy,#dateToConvert) = 1900)
BEGIN
SET #retValue = DATEDIFF(s,#date,#dateToConvert) + CASE WHEN DATEDIFF(D, 0, #dateToConvert) > 0 THEN DATEDIFF(D, 0, #dateToConvert) ELSE 0 END * 3600 * 24
END
ELSE
BEGIN
SET #retValue = DATEDIFF(s,#date,#dateToConvert)
END
RETURN #retValue
END
My expected output is,
* Please be noted, This Average column need to calculate, Suppose
when FkAssistTypeID = 1 and AddedLocalTime between 7/12/2019 and 7/14/2019 then by passing that row's AddedLocalTime and CompletedTime values fnTimetoSeconds taking time differance and take the summation of each time diffrences and divide it by count .
I need to add the above output to a temporary table. How can I do this?
I just tried this, but this is not the expected
select
b.BranchId AS ID,
b.BranchName,
count(case l.FkAssistTypeId when 1 then 1 end) as CountOf1,
SUM(CASE WHEN (l.FkAssistTypeId = 1) THEN COALESCE((dbo.fnTimetoSeconds(CompletedTime, AssistedTime, NULL)),0) ELSE 0 END) AS Average
from Branch b left join Lobby l
on b.BranchId = l.FkBranchId
where l.IsActive = 1 AND b.IsTestBranch = 0 AND CAST(l.AddedLocalTime as DATE) = '2019-07-12'
group by b.BranchId, b.BranchName
How about something like this :
SELECT Vals.ID,
Vals.BranchName ,
Vals.CountOf1,
CASE WHEN (Vals.CountOf1 = 0) THEN 0 ELSE Vals.mySum/Vals.CountOf1 END as AveSecs
INTO tmpTbl
FROM
(SELECT
b.BranchId AS ID,
b.BranchName,
count(l.FkAssistTypeI) as CountOf1,
SUM(DATEDIFF(second, CompletedTime, AssistedTime)) AS mySum
FROM Branch b
LEFT OUTER JOIN Lobby l
ON b.BranchId = l.FkBranchId
WHERE l.IsActive = 1
AND b.IsTestBranch = 0
AND l.FkAssistTypeI = 1
AND l.AddedLocalTime >= '2019/07/12'
AND l.AddedLocalTime < DATEADD(day, 1, '2019/07/15')
GROUP BY b.BranchId, b.BranchName) as Vals
The query first gets the count and the sum between 2019/07/12 00:00 and 2019/07/15 00:00 (not including). Then it inserts into a temp table (as you indicated) while calculating the average value. Note that you will need to test it and adjust it a bit since I have not run it. Also, I just used date diff to calculate the time in seconds, but you can use your function if needed. They should come up with the same value most times.
Not sure why you use the scalar function to get the date difference, even with the current logic, it's possible to do it in the query without the need of the function.
SELECT
b.BranchID
, b.BranchName
, ISNULL(l.CountOF1,0) CountOF1
, ISNULL(l.Average ,0) Average
FROM
#Branch b
LEFT JOIN (
SELECT
FkBranch
, IsActive
, COUNT(*) CountOF1
, AVG(DATEDIFF(SECOND, AddedLocalTime, CompletedTime)) Average
FROM #lobby l
WHERE
FkAssistTypeID = 1
AND AddedLocalTime BETWEEN '7/12/2019 00:00:00' AND '7/14/2019 23:59:59'
GROUP BY FkBranch, IsActive
) l ON l.FkBranch = b.BranchID AND l.IsActive = b.IsActive
WHERE
b.IsActive = 1
I would like to count the amount of pallets on a location where you can't see on the location how many pallets there are. The only way to find out is to assume that you can count backwards from the log where it's logged what pallets have been moved to that location.
Location quantity
LOCATION QUANTITY
Loc_1 20
Log
MOVED_TO_LOCATION QUANTITY PALLET_NUMBER
Loc_1 5 13
Loc_1 5 12
Loc_1 5 11
Loc_1 5 10 <-- Count the lines from here and up (count = 4).
Loc_1 5 9
...
What I tried so far was some CROSS APPLY where I joined the location to the log and counted the results. Which returned 13 (instead of 4) because it didn't count away the ones that were already gone. So how would I count backwards and only gets the first 4 rows?
-- Query returns 13 rows.
CROSS APPLY (
SELECT PALL_NUM
FROM LOG
WHERE MOVED_TO_LOCATION = LOCATION
ORDER BY TIMESTAMP DESC) LOG
I assume you could somehow incrementally count up the amount in the log from latest to earliest timestamp until the quantity is more or equal in the log to the current quantity on the location. But I'm not sure how to do that exactly.
Pseudocode
Quantity on location = 20
Row 1 in log: 5.
20 - 5 = 15 remaining.
Row 2 in log: 5.
15 - 5 = 10 remaining.
Row 3 in log: 5.
10 - 5 = 5 remaining.
Row 4 in log: 5.
5 - 5 = 0 remaining.
Then stop and return the rows.
Edit 1: Expected results and what I have
Location quantity
LOCATION QUANTITY
Loc_1 20
Loc_2 10
SELECT BS.LOCATION, TL.PALL_NUM, BS.QUANTITY
FROM BATCH_STOCK BS
LEFT OUTER JOIN TRANS_LOG TL
ON TL.ITEM_NUM = BS.ITEM_NUM AND LOC_TO = BS.LOCATION
ORDER BY BS.LOCATION
Result
LOCATION PALL_NUM QUANTITY
Loc_2 16 5
Loc_2 15 5
Loc_2 14 5
Loc_1 13 5
Loc_1 12 5
Loc_1 11 5
Loc_1 10 5
... ...
Loc_1 1 5
This is just an extended version from the first question. But shows the output I would get if I didn't count backwards in the log. So for example Loc_2 would only have 2 pallets with quantity still on them but return 3 pallets because 3 pallets have been moved to that location. And Loc_1 would return 13 pallets but the first 9 have already been "picked" so they aren't there anymore.
Expected result
LOCATION PALL_NUM
Loc_2 16
Loc_2 15
Loc_1 13
Loc_1 12
Loc_1 11
Loc_1 10
Edit 2: Table structure
BATCH_STOCK
LOCATION = PK NVARCHAR(13) NOT NULL
QTY = NUMERIC (9,2) NOT NULL
TRANS_LOG
PALL_NUM = BIGINT NOT NULL
LOC_TO = NVARCHAR(16) NULL
QTY = NUMERIC(9,2) NULL
You're looking for a running total? This will return you the 4 rows:
select * from (
select *,
sum(QUANTITY) over (order by PALLET_NUMBER desc) as Quant
from yourtable
) X where Quant <= 20
That only works with full pallets, if you need to include rows for partial quantities, this should work then:
select * from (
select *, isnull(lag(Quant) over (order by PALLET_NUMBER desc),0) as NextQuant
from (
select *,
sum(QUANTITY) over (order by PALLET_NUMBER desc) as Quant
from yourtable
) X
) Y where NextQuant < 15
Update, added examples with the new tables included in the question
select * from (
select *,
sum(QUANTITY) over (partition by LOCATION order by PALL_NUM desc) as Quant
from #TRANS_LOG
) X join #BATCH_STOCK S on S.QUANTITY >= X.Quant and S.LOCATION = X.LOCATION
select * from (
select *, isnull(lag(Quant) over (partition by LOCATION order by PALL_NUM desc),0) as NextQuant
from (
select *,
sum(QUANTITY) over (partition by LOCATION order by PALL_NUM desc) as Quant
from #TRANS_LOG
) X
) Y join #BATCH_STOCK S on S.QUANTITY > Y.NextQuant and S.LOCATION = Y.LOCATION
Results:
LOCATION PALL_NUM QUANTITY Quant LOCATION QUANTITY
1 Loc_1 13 5 5 Loc_1 20
2 Loc_1 12 5 10 Loc_1 20
3 Loc_1 11 5 15 Loc_1 20
4 Loc_1 10 5 20 Loc_1 20
5 Loc_2 16 5 5 Loc_2 10
6 Loc_2 15 5 10 Loc_2 10
LOCATION PALL_NUM QUANTITY Quant NextQuant LOCATION QUANTITY
1 Loc_1 13 5 5 0 Loc_1 20
2 Loc_1 12 5 10 5 Loc_1 20
3 Loc_1 11 5 15 10 Loc_1 20
4 Loc_1 10 5 20 15 Loc_1 20
5 Loc_2 16 5 5 0 Loc_2 10
6 Loc_2 15 5 10 5 Loc_2 10
Example that I made
Okay, Here is what i have tried so far. I have used recursive CTE to achieve the expected output.
Schema
CREATE TABLE #tLocationQty
(
Location VARCHAR(10),
Quantity int
)
INSERT INTO #tLocationQty VALUES('Loc_1',20)
INSERT INTO #tLocationQty VALUES('Loc_2',10)
Second table schema
CREATE TABLE #tLog
(
MoveToLocation VARCHAR(10),
Quantity int,
PALL_NUM int
)
INSERT INTO #tLog VALUES('Loc_1',5, 13)
INSERT INTO #tLog VALUES('Loc_1',5, 12)
INSERT INTO #tLog VALUES('Loc_1',5, 11)
INSERT INTO #tLog VALUES('Loc_1',5, 10)
INSERT INTO #tLog VALUES('Loc_1',5, 9)
INSERT INTO #tLog VALUES('Loc_2',5, 16)
INSERT INTO #tLog VALUES('Loc_2',5, 15)
and below is the query.
Note: - I have not use order by clause. You have to use it as per your requirement.
Query
;WITH myCTE
AS
(
SELECT Location, Quantity AS Qty FROM #tLocationQty
UNION ALL
SELECT MoveToLocation, (Qty - t.Quantity) AS Qty FROM #tLog t
INNER JOIN myCTE
ON myCTE.Location = t.MoveToLocation
WHERE myCTE.Qty > 0
),
DistLocation
AS
(
SELECT DISTINCT Location FROM myCTE WHERE qty > 0
),
RowCnt
AS
(
SELECT Location, COUNT(DISTINCT Qty) AS Cnt FROM myCTE WHERE qty > 0
GROUP BY Location
)
--SELECT * FROM RowCnt
SELECT * FROM DistLocation
CROSS APPLY
(
SELECT TOP (SELECT top 1 Cnt FROM RowCnt r WHERE r.location = location)
PALL_NUM
FROM #tLog t
WHERE t.MoveToLocation = location
) c
Output
Location PALL_NUM
Loc_1 13
Loc_1 12
Loc_1 11
Loc_1 10
Loc_2 15
Loc_2 16
I have a scenario where i'm splitting a number of results into quartilies using the SQL Server NTILE function below. The goal is to have an as equal number of rows in each class
case NTILE(4) over (order by t2.TotalStd)
when 1 then 'A' when 2 then 'B' when 3 then 'C' else 'D' end as Class
The result table is shown below and there is a (9,9,8,8) split between the 4 class groups A,B,C and D.
There are two results which cause me an issue, both rows have a same total std value of 30 but are assigned to different quartiles.
8 30 A
2 30 B
I'm wondering is there a way to ensure that rows with the same value are assigned to the same quartile? Can i group or partition by another column to get this behaviour?
Pos TotalStd class
1 16 A
2 23 A
3 21 A
4 29 A
5 25 A
6 26 A
7 28 A
8 30 A
9 29 A
1 31 B
2 30 B
3 32 B
4 32 B
5 34 B
6 32 B
7 34 B
8 32 B
9 33 B
1 36 C
2 35 C
3 35 C
4 35 C
5 40 C
6 38 C
7 41 C
8 43 C
1 43 D
2 48 D
3 45 D
4 47 D
5 44 D
6 48 D
7 46 D
8 57 D
You will need to re create the Ntile function, using the rank function.
The rank function gives the same rank for rows with the same value. The value later 'jumps' to the next rank as if you used row_number.
We can use this behavior to mimic the Ntile function, forcing it to give the same Ntile value to rows with the same value. However - this will cause the Ntile partitions to be with a different size.
See the example below for the new Ntile using 4 bins:
declare #data table ( x int )
insert #data values
(1),(2),
(2),(3),
(3),(4),
(4),(5)
select
x,
1+(rank() over (order by x)-1) * 4 / count(1) over (partition by (select 1)) as new_ntile
from #data
Results:
x new_ntile
---------------
1 1
2 1
2 1
3 2
3 2
4 3
4 3
5 4
Not sure what you're expecting to happen here, really. SQL Server has divided up the data into 4 groups of as-equal-size-as-possible, as you asked. What do you want to happen? Have a look at this example:
declare #data table ( x int )
insert #data values
(1),(2),
(2),(3),
(3),(4),
(4),(5)
select
x,
NTILE(4) over (order by x) as ntile
from #data
Results:
x ntile
----------- ----------
1 1
2 1
2 2
3 2
3 3
4 3
4 4
5 4
Now every ntile group shares a value with the one(s) next to it! But what else should it do?
Try this:
; with a as (
select TotalStd,Class=case ntile(4)over( order by TotalStd )
when 1 then 'A'
when 2 then 'B'
when 3 then 'C'
when 4 then 'D'
end
from t2
group by TotalStd
)
select d.*, a.Class from t2 d
inner join a on a.TotalStd=d.TotalStd
order by Class,Pos;
Here we have a table of 34 rows.
DECLARE #x TABLE (TotalStd INT)
INSERT #x (TotalStd) VALUES (16), (21), (23), (25), (26), (28), (29), (29), (30), (30), (31), (32), (32), (32), (32), (33), (34),
(34), (35), (35), (35), (36), (38), (40), (41), (43), (43), (44), (45), (46), (47), (48), (48), (57)
SELECT '#x', TotalStd FROM #x ORDER BY TotalStd
We want to divide into quartiles. If we use NTILE, the bucket sizes will be roughly the same size (8 to 9 rows each) but ties are broken arbitrarily:
SELECT '#x with NTILE', TotalStd, NTILE(4) OVER (ORDER BY TotalStd) quantile FROM #x
See how 30 appears twice: once in quantile 1 and once in quantile 2. Similarly, 43 appears both in quantiles 3 and 4.
What I ought to find is 10 items in quantile 1, 8 in quantile 2, 7 in quantile 3 and 9 in quantile 4 (i.e. not a perfect 9-8-9-8 split, but such a split is impossible if we are not allowed to break ties arbitrarily). I can do it using NTILE to determine cutoff points in a temporary table:
DECLARE #cutoffs TABLE (quantile INT, min_value INT, max_value INT)
INSERT #cutoffs (quantile, min_value)
SELECT y.quantile, MIN(y.TotalStd)
FROM (SELECT TotalStd, NTILE(4) OVER (ORDER BY TotalStd) AS quantile FROM #x) y
GROUP BY y.quantile
-- The max values are the minimum values of the next quintiles
UPDATE c1 SET c1.max_value = ISNULL(C2.min_value, (SELECT MAX(TotalStd) + 1 FROM #x))
FROM #cutoffs c1 LEFT OUTER JOIN #cutoffs c2 ON c2.quantile - 1 = c1.quantile
SELECT '#cutoffs', * FROM #cutoffs
We'll use the the boundary values in the #cutoffs table to create the final table:
SELECT x.TotalStd, c.quantile FROM #x x
INNER JOIN #cutoffs c ON x.TotalStd >= c.min_value AND x.TotalStd < c.max_value
I want to achieve a full numeric scale from 0 to the max number in the table.
Let's say we have a table T with two fields named x and y
select x,y
from t
would show us lets say the results
X Y
3 11
5 23
7 45
9 1
10 34
I found this query to build sequential numbers:
With T_Misparim As
(Select 1 N
Union All
Select N+1 N
From T_Misparim
Where N<1000)
Select N
From T_Misparim
Option (MaxRecursion 0);
from this source : http://www.sqlserver.co.il/?p=3296
My bottom line is, how do i integrate the two queries into a single query to give
right outer join :
N X Y
0 null 0
1 null 0
2 null 0
3 3 11
4 null 0
5 5 23
6 null 0
7 7 45
8 null 0
9 9 1
10 10 34
You can just LEFT JOIN with the ordinal number CTE;
select 3 as X, 11 as Y into #TEST
insert #TEST values (5,23),(7,45),(9,1),(10,34)
;with NUMS(n) as (
select 0 union all
select 1 + n from NUMS where n < 50
)
select
NUMS.n N,
T.X,
isnull(T.Y, 0) Y
from NUMS
left join #TEST T on (T.X = NUMS.n)
option (maxrecursion 50)
For
N X Y
0 NULL 0
1 NULL 0
2 NULL 0
3 3 11
4 NULL 0
5 5 23
6 NULL 0
7 7 45
8 NULL 0
9 9 1
10 10 34