SQL Server script not working as expected - sql-server

I have this little script that shall return the first number in a column of type int which is not used yet.
SELECT t1.plu + 1 AS plu
FROM tovary t1
WHERE NOT EXISTS (SELECT 1 FROM tovary t2 WHERE t2.plu = t1.plu + 1)
AND t1.plu > 0;
this returns the unused numbers like
3
11
22
27
...
The problem is, that when I make a simple select like
SELECT plu
FROM tovary
WHERE plu > 0
ORDER BY plu ASC;
the results are
1
2
10
20
...
Why the first script isn't returning some of free numbers like 4, 5, 6 and so on?

Compiling a formal answer from the comments.
Credit to Larnu:
It seems what the OP really needs here is an (inline) Numbers/Tally (table) which they can then use a NOT EXISTS against their table.
Sample data
create table tovary
(
plu int
);
insert into tovary (plu) values
(1),
(2),
(10),
(20);
Solution
Isolating the tally table in a common table expression First1000 to produce the numbers 1 to 1000. The amount of generated numbers can be scaled up as needed.
with First1000(n) as
(
select row_number() over(order by (select null))
from ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) a(n) -- 10^1
cross join ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) b(n) -- 10^2
cross join ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) c(n) -- 10^3
)
select top 20 f.n as Missing
from First1000 f
where not exists ( select 'x'
from tovary
where plu = f.n);
Using top 20 in the query above to limit the output. This gives:
Missing
-------
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
19
21
22
23
24

Related

Accumulative Update for previous records

I have table that shows these information
Month NewClients OnHoldClients
5-2017 10 2
6-2017 16 4
7-2017 11 1
8-2017 15 6
9-2017 18 7
I am trying to find the accumulative total for each month
which is
(NewClients - OnHoldClients) + Previous Month Total
Something like this
Month NewClients OnHoldClients Total
5-2017 10 2 8
6-2017 16 4 20
7-2017 11 1 30
8-2017 15 6 39
9-2017 18 7 50
the query i tried to build was something like this but I think should be an easier way to do that
UPDATE MyTable
SET Total = (SELECT TOP 1 Total FROM MyTable B WHERE B.Month < A.Month) + NewClients - OnHoldClients
FROM MyTable A
Before we begin, note the mere fact that you're facing such calculative problem is a symptom that maybe you don't have the best possible design. Normally for this purpose calculated values are being stored along the way as the records are inserted. So i'd say you'd better have a total field to begin with and calculate it as records amass.
Now let's get down to the problem at hand. i composed a query which does that nicely but it's a bit verbose due to recursive nature of the problem. However, it yields the exact expected result:
DECLARE #dmin AS date = (SELECT min(mt.[Month]) from dbo.MyTable mt);
;WITH cte(_Month, _Total) AS (
SELECT mt.[Month] AS _Month, (mt.NewClients - mt.OnHoldClients) AS _Total
FROM dbo.MyTable mt
WHERE mt.[Month] = #dmin
UNION ALL
SELECT mt.[Month] AS _Month, ((mt.NewClients - mt.OnHoldClients) + ccc._Total) AS _Total
FROM dbo.MyTable mt
CROSS APPLY (SELECT cc._Total FROM (SELECT c._Total,
CAST((row_number() OVER (ORDER BY c._Month DESC)) AS int) as _Rank
FROM cte c WHERE c._Month < mt.[Month]) as cc
WHERE cc._Rank = 1) AS ccc
WHERE mt.[Month] > #dmin
)
SELECT c._Month, max(c._Total) AS Total
FROM cte c
GROUP BY c._Month
It is a recursive CTE structure that goes about each record all along the way to the initial month and adds up to the final Total value. This query only includes Month and Total fields but you can easily add the other 2 to the list of projection.
Try this
;WITH CTE([Month],NewClients,OnHoldClients)
AS
(
SELECT '5-2017',10,2 UNION ALL
SELECT '6-2017',16,4 UNION ALL
SELECT '7-2017',11,1 UNION ALL
SELECT '8-2017',15,6 UNION ALL
SELECT '9-2017',18,7
)
SELECT [Month],
NewClients,
OnHoldClients,
SUM(MonthTotal)OVER( ORDER BY [Month]) AS Total
FROM
(
SELECT [Month],
NewClients,
OnHoldClients,
SUM(NewClients-OnHoldClients)OVER(PArtition by [Month] Order by [Month]) AS MonthTotal
FROM CTE
)dt
Result,Demo:http://rextester.com/DKLG54359
Month NewClients OnHoldClients Total
--------------------------------------------
5-2017 10 2 8
6-2017 16 4 20
7-2017 11 1 30
8-2017 15 6 39
9-2017 18 7 50

Convert hierarchy defined by position to SQL hierarchy id defined

I have a lot of data from an old system which defines the data in a Bill of Materials by the position it exists in a table.
The BoM data table coming from the old system looks like
ID level ItemNumber
1 1 TopItem
2 .2 FirstChildOfTop
3 .2 2ndChildofTop
4 .2 3ChildOfTop
5 ..3 1stChildof3ChildofTop
6 ..3 2ndChildof3ChildofTop
7 .2 4thChildofTop
8 ..3 1stChildof4ChildTop
9 ...4 1stChildof4ChildTop
10 ..3 2ndChildof4ChildofTop
11 .2 5thChildofTop
12 ..3 1stChildof5thChildofTop
13 ...4 1stChildof1stChildof5thChildofTop
14 ..3 2ndChildof5thChildofTop
15 1 2ndTopItem
16 1 3rdTopItem
In my example the ID is consecutive, the real data the ID can be broken but always lowest to highest as that is how the hierarchy is defined.
By using some simple code to replace the level number with tabs we can get visual hierarchy
1 TopItem
2 FirstChildOfTop
3 2ndChildofTop
4 3ChildOfTop
5 1stChildof3ChildofTo
6 2ndChildof3ChildofTo
7 4thChildofTop
8 1stChildof4ChildTop
9 1stChildof4ChildTop
10 2ndChildof4ChildofTo
11 5thChildofTop
12 1stChildof5thChildof
13 1stChildof1stChildof
14 2ndChildof5thChildof
15 2ndTopItem
16 3rdTopItem
As I have about 5,000 of these lists and they are all between 25 and 55 thousand lines long, I need some code to convert this hierarchy to use sql HierarchyID so we can query at any level in the list. At the moment I hope my explanation shows, you have to work from the top to find in the Item is 2nd, 3rd or some other level and if it has any children. The items in the third column exist in a simple Item Master table but its role in a BoM is defined in these tables only.
I'd offer some code but all my attempts and conversion have failed miserably. I'd claim I'm OK a set based queries
The target is Microsoft SQL 2014
The primary aim is to data warehouse the data but enable to people to find sub-assemblies and where used.
Edit:
In answer to Anthony Hancock's very pertinent question I did some work. Please consider the following
ID level ItemNumber sampH lft rgt
1 1 TopItem 1/2 2 28
2 .2 FirstChildOfTop 1/2/3 3 4
3 .2 2ndChildofTop 1/2/3 5 6
4 .2 3ChildOfTop 1/2/3 7 11
5 ..3 1stChildof3ChildofTop 2/3/4 8 9
6 ..3 2ndChildof3ChildofTop 2/3/4 10 11
7 .2 4thChildofTop 1/2/3 13 20
8 ..3 1stChildof4ChildTop 2/3/4 14 17
9 ...4 1stChildof4ChildTop 3/4/5 15 16
10 ..3 2ndChildof4ChildofTop 2/3/4 18 19
11 .2 5thChildofTop 1/2/3/ 20 25
12 ..3 1stChildof5thChildofTop 2/3/4 21 24
13 ...4 1stChildof1stChildof5thChildofTop 3/4/5 22 23
14 ..3 2ndChildof5thChildofTop 2/3/4 26 27
15 1 2ndTopItem 1/2 2 28
16 1 3rdTopItem 1/2 2 28
17 0 verytop 1/ 1 29
Apologies for the awful formatting
1) I have added at line 17 the item we are making - ie this BoM makes the 'verytop' item - so I have renumbered the 'level'
2) I have added in the column 'sampH' column my hand edited PathEnumeratedTree values
3) In the two columns 'lft' and 'rgt' I have added some identifiers of NestedSets data
Please forgive if my hand edited columns aren't correct.
My aim is to get a structure so that someone can query these many deep list to find where an item sits in the tree and what are its children. So I'm open to whatever works.
My testing of the NestedSets - so far - has shown I can do stuff like this:
-- Children of a given parent ItemNumber
Select c.itemnumber, ' is child of 2ndTopItem'
from [dbo].[Sample] as p, [dbo].[Sample] as c
where (c.lft between p.lft and p.rgt)
and (c.lft <> p.lft)
and p.ItemNumber = '2ndTopItem'
But I am completely open to any suggestions how to enumerate the tree structure.
Try the following code:
declare #Source table (
Id int ,
[Level] varchar(20) ,
[Name] varchar(50)
);
declare #Target table (
Id int ,
[Level] int ,
[Name] varchar(50) ,
ParentId int ,
Hid hierarchyid ,
primary key (Id),
unique ([Level], Id),
unique (ParentId, Id)
);
-- 1. The Test Data (Thanks Anthony Hancock for it)
insert into #Source
values
( 1 , '1' , 'TopItem' ),
( 2 , '.2' , 'FirstChildOfTop' ),
( 3 , '.2' , '2ndChildofTop' ),
( 4 , '.2' , '3ChildOfTop' ),
( 5 , '..3' , '1stChildof3ChildofTop' ),
( 6 , '..3' , '2ndChildof3ChildofTop' ),
( 7 , '.2' , '4thChildofTop' ),
( 8 , '..3' , '1stChildof4ChildTop' ),
( 9 , '...4' , '1stChildof4ChildTop' ),
( 10 , '..3' , '2ndChildof4ChildofTop' ),
( 11 , '.2' , '5thChildofTop' ),
( 12 , '..3' , '1stChildof5thChildofTop' ),
( 13 , '...4' , '1stChildof1stChildof5thChildofTop' ),
( 14 , '..3' , '2ndChildof5thChildofTop' ),
( 15 , '1' , '2ndTopItem' ),
( 16 , '1' , '3rdTopItem' );
-- 2. Insert the Test Data to the #Target table
-- with converting of the Level column to int data type
-- to use it as an indexed column in the query # 3
-- (once there are millions of records, that index will be highly useful)
insert into #Target (Id, [Level], [Name])
select
Id,
[Level] = cast(replace([Level],'.','') as int),
[Name]
from
#Source
-- 3. Calculate the ParentId column and update the #Target table
-- to use the ParentId as an indexed column in the query # 4
update t set
ParentId = (
select top 1 Id
from #Target as p
where p.Id < t.Id and p.[Level] < t.[Level]
order by p.Id desc )
from
#Target t;
-- 4. Calculate the Hid column
-- based on the ParentId link and in accordance with the Id order
with Recursion as
(
select
Id ,
ParentId ,
Hid = cast(
concat(
'/',
row_number() over (order by Id),
'/'
)
as varchar(1000)
)
from
#Target
where
ParentId is null
union all
select
Id = t.Id ,
ParentId = t.ParentId ,
Hid = cast(
concat(
r.Hid,
row_number() over (partition by t.ParentId order by t.Id),
'/'
)
as varchar(1000)
)
from
Recursion r
inner join #Target t on t.ParentId = r.Id
)
update t set
Hid = r.Hid
from
#Target t
inner join Recursion r on r.Id = t.Id;
-- 5. See the result ordered by Hid
select
Id ,
[Level] ,
[Name] ,
ParentId ,
Hid ,
HidPath = Hid.ToString()
from
#Target
order by
Hid;
Read more about Combination of Id-ParentId and HierarchyId Approaches to Hierarchical Data
Using your example data to create a test table and then create parent IDs for each row I think this is what you are after? The big caveat is that this is entirely dependent on your table being ordered correctly for the hierarchies but I don't see any other options from the information provided.
DROP TABLE IF EXISTS TEST;
CREATE TABLE TEST
(
ID INT
,[Level] VARCHAR(20)
,ItemNumber VARCHAR(50)
)
;
INSERT INTO TEST
(ID,[Level],ItemNumber)
VALUES
(1,'1','TopItem')
,(2,'.2','FirstChildOfTop')
,(3,'.2','2ndChildofTop')
,(4,'.2','3ChildOfTop')
,(5,'..3','1stChildof3ChildofTop')
,(6,'..3','2ndChildof3ChildofTop')
,(7,'.2','4thChildofTop')
,(8,'..3','1stChildof4ChildTop')
,(9,'...4','1stChildof4ChildTop')
,(10,'..3','2ndChildof4ChildofTop')
,(11,'.2','5thChildofTop')
,(12,'..3','1stChildof5thChildofTop')
,(13,'...4','1stChildof1stChildof5thChildofTop')
,(14,'..3','2ndChildof5thChildofTop')
,(15,'1','2ndTopItem')
,(16,'1','3rdTopItem')
;
SELECT *
,V.ParentID
FROM TEST AS T
OUTER APPLY
(
SELECT TOP 1 ID AS ParentID
FROM TEST AS _T
WHERE _T.ID < T.ID
AND REPLACE(_T.[Level],'.','') < REPLACE(T.[Level],'.','')
ORDER BY _T.ID DESC
) AS V
ORDER BY T.ID
;
DROP TABLE IF EXISTS TEST;

How to group by on consecutive values in SQL

I have a table in SQL Server 2014 with sample data as follows.
WK_NUM | NET_SPRD_LCL
10 0
11 1500
12 3600
13 3800
14 4000
I am trying to code a bonus structure at work where I need to group on WK_NUM. So, if I see NET_SPRD_LCL > 3500 for two consecutive WK_NUMs WHERE WK_NUM < 27, I need to output 2000. In this example, since NET_SPRD_LCL for WK_NUM 12 and 13 are both greater than 3500, the SQL should output 2000 and exit. So, it should ignore the fact that WK_NUM 13 and 14 also satisfy the condition that NET_SPRD_LCL > 3500.
I would appreciate any help with this.
Assuming you mean consecutive line 1, 2, 3, 4, 5 ... etc. and NOT
1, 3, 5, 8, 12, etc.
then, if you don't need to know which pair of consecutive records it was:
Select case when exists
(Select * from table f
join table n
on n.Wk_Num = f.Wk_Num + 1
and n.NET_SPRD_LCL > 3500
and f.NET_SPRD_LCL > 3500
and n.Wk_Num < 27
then 2000 else null end
If you do need to identify the pair of records, then:
Select f.wk_Num firstWorkNbr, f.NET_SPRD_LCL firstNetSpread,
n.wk_Num nextWorkNbr, n.NET_SPRD_LCL nextNetSpread
from table f
join table n
on n.Wk_Num = f.Wk_Num + 1
and n.NET_SPRD_LCL > 3500
and f.NET_SPRD_LCL > 3500
and n.Wk_Num < 27
Where not exists
(Select * from table f0
join table n0
on n0.Wk_Num = f0.wk_Num + 1
and n0.WkNum < f.Wk_Num))
on the other hand if the consecutive is simply increasing, then it's a bit harder. You need to use a subquery to determine the next consecutive record...
Select case when exists
(Select * from table f
join table n
on n.Wk_Num = (Select Min(Wk_Num) from table
Where Wk_Num > f.Wk_Num)
and n.NET_SPRD_LCL > 3500
and f.NET_SPRD_LCL > 3500
and n.Wk_Num < 27
then 2000 else null end
and if you need to fetch the data for the specific first pair of records that qualify (the 2000 at the end is unnecessary since if there is no qualifying pair nothing will be returned.)
Select f.wk_Num firstWorkNbr, f.NET_SPRD_LCL firstNetSpread,
n.wk_Num nextWorkNbr, n.NET_SPRD_LCL nextNetSpread, 2000 outValue
from table f
join table n
on n.Wk_Num = (Select Min(Wk_Num) from table
Where Wk_Num > f.Wk_Num)
and n.NET_SPRD_LCL > 3500
and f.NET_SPRD_LCL > 3500
and n.Wk_Num < 27
Where not exists
(Select * from table f0
join table n0
on n0.Wk_Num = (Select Min(Wk_Num) from table
Where Wk_Num > f0.Wk_Num)
and n0.WkNum < f.Wk_Num))
First of all, when you say you want your query to 'output' and 'exit', it makes me think you are approaching t-sql as a procedural language, which it is not. Good t-sql queries are nearly always set based.
In any case, before the query, let me add what is helpful for others to work with the data to build queries:
DECLARE #t TABLE (WK_NUM INT, NET_SPRD_LCL INT);
INSERT INTO #t VALUES
(10, 0),
(11, 1500),
(12, 3600),
(13, 3800),
(14, 4000);
You say you are using SQL Server 2014, which means you have relevant window functions at your disposal. The one I am using (LAG) will have superior performance to using subqueries, which, if you insist on using, can be greatly improved by using TOP (1) with ORDER BY and an appropriate index instead of using a MIN function over the whole dataset. With tiny amounts of data you won't notice a difference, but on a real business system it will be obvious.
Adjusted to provide the 2000 bonus on the correct line after OP's clarification:
WITH cteTemp AS
(
SELECT WK_NUM
, thisValue = NET_SPRD_LCL
, lastValue = LAG(NET_SPRD_LCL) OVER(ORDER BY WK_NUM)
FROM #t
WHERE WK_NUM < 27
)
, cteBonusWeek AS
(
SELECT TOP (1)
WK_NUM
, bonus = 2000
FROM cteTemp
WHERE thisValue > 3500 AND lastValue > 3500
ORDER BY WK_NUM
)
SELECT t.WK_NUM
, t.NET_SPRD_LCL
, bonus = COALESCE(b.bonus, 0)
FROM #t AS t
LEFT JOIN cteBonusWeek AS b
ON b.WK_NUM = t.WK_NUM;

SQL: return IDs whose val=min(exp)

Given a table like
pkg#, time
0, 20
1, 23
2, 34
3, 35
4, 59
I want to know the pkg# who has max/min time difference to its successor pkg (gap between 2 consecutive pkgs)
In this case, pkg-2 has min time difference (1), and pkg-3 has max time difference (14)
What's the sql that can return pkg# for min/max time difference to its next pkg?
If you are on SQL SERVER 2012 or above, you can try LEAD function here to get the next row value to align in your current row:
SELECT *, LEAD([time]) OVER(ORDER BY [pkg#]) as nexttime
FROM [your_table]
will yield something like this:
pkg time nexttime
0 20 23
1 23 34
2 34 35
3 35 59
4 59 NULL
Now compare these two columns values should give you what you want. (Note last row will have nexttime = NULL since there's no more row to get value from, so just filter it out when querying).
Assume new table name is new_table, to get max diff:
select top 1 *, nexttime-time as diff
from new_table
where nexttime is not null
order by (nexttime-time) desc
and to get min diff just order by nexttime-time
A slight twist on #xbb 's answer:
CREATE TABLE #t ( Pkg INT, Time INT );
INSERT #t ( Pkg, Time )
VALUES ( 0, 20 ),
( 1, 23 ),
( 2, 34 ),
( 3, 35 ),
( 4, 59 );
SELECT Pkg
, Time
, Time - LAG(Time) OVER ( ORDER BY Pkg ) AS TimeSincePrevious
, ABS(time - LEAD(Time) OVER ( ORDER BY Pkg )) AS TimeUntilNext
FROM #t;
DROP TABLE #t;
Will yield the result:
Pkg Time TimeSincePrevious TimeUntilNext
0 20 NULL 3
1 23 3 11
2 34 11 1
3 35 1 24
4 59 24 NULL
Take a look at solution below - I decomposed query into three steps:
WITH Ordered AS
(
SELECT ROW_NUMBER() OVER (ORDER BY pkg) rowNum, pkg, [time] FROM Test
),
Diffs AS
(
SELECT T1.pkg,
T2.[time]-T1.[time] diff,
MIN(T2.[time]-T1.[time]) OVER () minimum,
MAX(T2.[time]-T1.[time]) OVER () maximum
FROM Ordered T1
JOIN Ordered T2 ON T1.rowNum = T2.rowNum-1
)
SELECT pkg, diff FROM Diffs
WHERE diff=minimum OR diff=maximum
ORDER by diff
Number of rows
Join with offset 1, calculate diff, MIN and MAX
Filter rows not equal to min or max
Query may return more rows if tie occurs. Ties can be simply removed by replacing final SELECT with:
...
SELECT MIN(pkg) pkg, diff FROM Diffs
WHERE diff=minimum OR diff=maximum
GROUP BY diff
ORDER by diff

SQL Server NTILE - Same value in different quartile

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

Resources