The use of pivot data to calculate a column - tsql

I have a PIVOT
SELECT txtSchoolID, intSubjectID, txtPreName, txtSurname, txtForm, [636] AS Effort, [637] AS Focus, [638] AS Participation, [639] AS Groupwork, [640] AS Rigour, [641] AS Curiosity, [642] AS Initiative,
[643] AS SelfOrganisation, [644] as Perserverance
FROM (SELECT txtSchoolID, intSubjectID, intGradeTransposeValue, intGradeID, txtForm, txtPreName, txtSurname FROM VwReportsManagementAcademicReports
WHERE (intReportCycleID = 142) AND (intReportProgress = 1)) p
PIVOT
(AVG (intGradeTransposeValue)
FOR intGradeID IN ([636], [637], [638], [639], [640], [641], [642], [643], [644] )
) AS pvt
I need to now use values from my pivot to produce additional columns. I want to create a column called FocusLH and populate it with a value based on a calculation. The calculation is - if the result of Effort+Focus=2 then assign the value 5 in FocusLH, if the result is 3 assigns the value 4 etc etc
Is this possible?

I was putting the CASE statement in the wrong place and needed to use the intGradeID's instead of the alias. This works now
SELECT txtSchoolID, intSubjectID, txtPreName, txtSurname, txtForm, [636] AS Effort, [637] AS Focus, [638] AS Participation, [639] AS Groupwork, [640] AS Rigour, [641] AS Curiosity, [642] AS Initiative,
[643] AS SelfOrganisation, [644] as Perserverance,
CASE WHEN [636] + [637] = 2 THEN 5
WHEN [636] + [637] =3 THEN 4
WHEN [636] + [637] =4 THEN 3
WHEN [636] + [637] =5 THEN 2
ELSE NULL END AS FocusLH
FROM (SELECT txtSchoolID, intSubjectID, intGradeTransposeValue, intGradeID, txtForm, txtPreName, txtSurname
FROM VwReportsManagementAcademicReports
WHERE (intReportCycleID = 142) AND (intReportProgress = 1)) p
PIVOT
(MAX (intGradeTransposeValue)
FOR intGradeID IN ([636], [637], [638], [639], [640], [641], [642], [643], [644] )
) AS pvt

Related

Group and Stuff multiple rows based on Count condition

I have a script that runs every 10 minutes and returns table with events from past 24 hours (marked by the script run time)
ID Name TimeOfEvent EventCategory TeamColor
1 Verlene Bucy 2015-01-30 09:10:00.000 1 Blue
2 Geneva Rendon 2015-01-30 09:20:00.000 2 Blue
3 Juliane Hartwig 2015-01-30 09:25:00.000 3 Blue
4 Vina Dutton 2015-01-30 12:55:00.000 2 Red
5 Cristin Lewis 2015-01-30 15:50:00.000 2 Red
6 Reiko Cushman 2015-01-30 17:10:00.000 1 Red
7 Mallie Temme 2015-01-30 18:35:00.000 3 Blue
8 Keshia Seip 2015-01-30 19:55:00.000 2 Blue
9 Rosalia Maher 2015-01-30 20:35:00.000 3 Red
10 Keven Gabel 2015-01-30 21:25:00.000 3 Red
Now I'd like to select two groups of Names based on these conditions:
1) Select Names from same EventCategory having 4 or more records in past 24 hours.
2) Select Names from same EventCategory and same TeamColor having 2 or more records in past 1 hour.
So my result would be:
4+per24h: Geneva Rendon, Vina Dutton, Cristin Lewis, Keshia Seip EventCategory = 2
4+per24h: Juliane Hartwig, Mallie Temme, Rosalia Maher, Keven Gabel EventCategory = 3
2+per1h: Rosalia Maher, Keven Gabel EventCategory = 3, TeamColor = Red
For the first one, I have written this:
SELECT mt.EventCategory, MAX(mt.[name]), MAX(mt.TimeOfEvent), MAX(mt.TeamColor)
FROM #mytable mt
GROUP BY mt.EventCategory
HAVING COUNT(mt.EventCategory) >= 4
because I don't care for the actual time as long as it's in the past 24 hours (and it always is), but I have trouble stuffing the names in one row.
The second part, I have no idea how to do. Because the results need to have both same EventCategory and TeamColor and also be limited by the one hour bracket.
this is possible, but you mix two separate issues. Here you find them combined with UNION:
Just paste this into an empty query window and execute. Adapt to your needs:
DECLARE #tbl TABLE(ID INT,Name VARCHAR(100),TimeOfEvent DATETIME,EventCategory INT,TeamColor VARCHAR(10));
INSERT INTO #tbl VALUES
(1,'Verlene Bucy','2015-01-30T09:10:00.000',1,'Blue')
,(2,'Geneva Rendon','2015-01-30T09:20:00.000',2,'Blue')
,(3,'Juliane Hartwig','2015-01-30T09:25:00.000',3,'Blue')
,(4,'Vina Dutton','2015-01-30T12:55:00.000',2,'Red')
,(5,'Cristin Lewis','2015-01-30T15:50:00.000',2,'Red')
,(6,'Reiko Cushman','2015-01-30T17:10:00.000',1,'Red')
,(7,'Mallie Temme','2015-01-30T18:35:00.000',3,'Blue')
,(8,'Keshia Seip','2015-01-30T19:55:00.000',2,'Blue')
,(9,'Rosalia Maher','2015-01-30T20:35:00.000',3,'Red')
,(10,'Keven Gabel','2015-01-30T21:25:00.000',3,'Red');
WITH Extended AS
(
SELECT *
,DATEDIFF(MINUTE,'2015-01-30T21:26:00.000',TimeOfEvent) AS MinuteDiff --use GETDATE() here...
,COUNT(*) OVER(PARTITION BY EventCategory) AS CountCategory
FROM #tbl AS tbl
)
,Filtered24Hours AS
(
SELECT *
FROM Extended
WHERE CountCategory >=4
)
,Filtered60Mins AS
(
SELECT *
FROM Extended
WHERE MinuteDiff >=-60
AND CountCategory >=2
)
SELECT DISTINCT (SELECT COUNT(*) FROM Filtered24Hours AS x WHERE x.EventCategory=outerSource.EventCategory) AS CountNames
,'per24h' AS TimeIntervall
,STUFF((
SELECT ' ,' + innerSource.Name
FROM Filtered24Hours AS innerSource
WHERE innerSource.EventCategory=outerSource.EventCategory
ORDER BY innerSource.TimeOfEvent
FOR XML PATH('')
),1,2,'') AS Names
,EventCategory
,NULL
FROM Filtered24Hours AS outerSource
UNION
SELECT DISTINCT (SELECT COUNT(*) FROM Filtered60Mins AS x WHERE x.EventCategory=outerSource.EventCategory)
,'per1h'
,STUFF((
SELECT ' ,' + innerSource.Name
FROM Filtered60Mins AS innerSource
WHERE innerSource.EventCategory=outerSource.EventCategory
ORDER BY innerSource.TimeOfEvent
FOR XML PATH('')
),1,2,'')
,EventCategory
,TeamColor
FROM Filtered60Mins AS outerSource
The result
Count Interv Names Category Team
4 per24h Geneva Rendon ,Vina Dutton ,Cristin Lewis ,Keshia Seip 2 NULL
4 per24h Juliane Hartwig ,Mallie Temme ,Rosalia Maher ,Keven Gabel 3 NULL
2 per1h Rosalia Maher ,Keven Gabel 3 Red

Building totals based on condition in another row

Really struggling to understand the best way of doing this...
I have a table of data
**StudentID AssessmentCode ResultGroup Result**
46933 12ENG IBLevel HL
46933 12ENG Mark 6
46933 12ECO IBLevel HL
46933 12ECO Mark 5
46933 12GEO IBLevel SL
46933 12GEO Mark 6
46933 12LAN IBLevel HL
46933 12LAN Mark 4
46933 12PED IBLevel SL
46933 12PED Mark 5
46933 12SCI IBLevel SL
46933 12SCI Mark 3
67767 12FRE IBLevel HL
67767 12FRE Mark 4
67767 12MAT IBLevel SL
67767 12MAT Mark 5
and so on...
Unfortunately the result column holds 2 different bits of info. The level a student does (HL = High Level; SL = Standard Level) and then the result for that subject at that level. Note that for each student, 2 rows are generated per subject a student does, one row has the IBLevel and the next row has the result for that level.
How can I rearrange the data to get something like
StudentID HLResult SLResult TotalResult CountofHL CountofSL
46933 15 14 29 3 3
67767 13 10 23 4 2
So each student has one row of data with totals for HL, SL, both together and then a count of the number of HL and SL subjects. As I said, really not sure of the best way of going about this. In the end, I would also like to extend this to get some columns which, based on the results give some warning messages eg the TotalResult has to be great than 24 so I would love a column that simply returns whether this has been achieved or not...
StudentID HLResult SLResult TotalResult CountofHL CountofSL MoreThan24
46933 15 14 29 3 3 True
67767 13 10 23 4 2 False
Any help would be greatly appreciated...
Your table seems to have some serious normalization issues. You have to perform an INNER JOIN on AssessmentCode first:
SELECT t1.StudentID, t1.AssessmentCode,
t1.Result AS Level, CAST(t2.Result AS INT) AS Mark
FROM (
SELECT StudentID, AssessmentCode, Result
FROM mytable
WHERE ResultGroup = 'IBLevel' ) AS t1
INNER JOIN (
SELECT StudentID, AssessmentCode, Result
FROM mytable
WHERE ResultGroup = 'Mark' ) AS t2
ON t1.StudentID = t2.StudentID AND t1.AssessmentCode = t2.AssessmentCode
to get one row per AssessmentCode.
Output:
StudentID AssessmentCode Level Mark
-----------------------------------------
46933 12ENG HL 6
46933 12ECO HL 5
46933 12GEO SL 6
46933 12LAN HL 4
46933 12PED SL 5
46933 12SCI SL 3
67767 12FRE HL 4
67767 12MAT SL 5
You can now wrap the above query in a CTE and perform conditional aggregation to get required result:
;WITH CTE AS (
... above query here
)
SELECT StudentID,
SUM(CASE WHEN Level = 'HL' THEN Mark ELSE 0 END) AS HLResult,
SUM(CASE WHEN Level = 'SL' THEN Mark ELSE 0 END) AS SLResult,
SUM(Mark) AS TotalResult,
COUNT(CASE WHEN Level = 'HL' THEN 1 END) AS CountofHL,
COUNT(CASE WHEN Level = 'SL' THEN 1 END) AS CountofSL,
CASE WHEN SUM(Mark) > 24 THEN 'True'
ELSE 'False'
END AS MoreThan24
FROM CTE
GROUP BY StudentID
Output:
StudentID HLResult SLResult TotalResult CountofHL CountofSL MoreThan24
46933 15 14 29 3 3 True
67767 4 5 9 1 1 False
Demo here
My Version of Your Table
DECLARE #yourTable TABLE
(
StudentID INT,
AssessmentCode CHAR(5),
ResultGroup VARCHAR(10),
Result VARCHAR(5)
)
INSERT INTO #yourTable
VALUES (46933,'12ENG','IBLevel','HL'),
(46933,'12ENG','Mark','6'),
(46933,'12ECO','IBLevel','HL'),
(46933,'12ECO','Mark','5'),
(46933,'12GEO','IBLevel','SL'),
(46933,'12GEO','Mark','6'),
(46933,'12LAN','IBLevel','HL'),
(46933,'12LAN','Mark','4'),
(46933,'12PED','IBLevel','SL'),
(46933,'12PED','Mark','5'),
(46933,'12SCI','IBLevel','SL'),
(46933,'12SCI','Mark','3'),
(67767,'12FRE','IBLevel','HL'),
(67767,'12FRE','Mark','4'),
(67767,'12MAT','IBLevel','SL'),
(67767,'12MAT','Mark','5');
Actual Query
WITH CTE_ResultValue
AS
(
SELECT StudentID,
AssessmentCode,
ResultGroup,
CAST(Result AS INT) AS ResultValue
FROM #yourTable
WHERE ISNUMERIC(Result) = 1
),
CTE_IBLevel
AS
(
SELECT StudentID,
AssessmentCode,
Result AS IBLevel
FROM #yourTable
WHERE ISNUMERIC(Result) = 0
),
CTE_Normalized
AS
(
SELECT A.StudentID,
A.AssessmentCode,
A.ResultGroup,
A.ResultValue,
B.IBLevel
FROM CTE_ResultValue AS A
INNER JOIN CTE_IBLevel AS B
ON A.StudentID = B.StudentID
AND A.AssessmentCode = B.AssessmentCode
)
SELECT [StudentID],
[HLResult] = SUM(CASE WHEN IBLevel = 'HL' THEN ResultValue END),
[SLResult] = SUM(CASE WHEN IBLevel = 'SL' THEN ResultValue END),
[TotalResult] = SUM(ResultValue),
[CountOfHL] = SUM(CASE WHEN IBLevel = 'HL' THEN 1 END),
[CountOfSL] = SUM(CASE WHEN IBLevel = 'SL' THEN 1 END)
FROM CTE_Normalized
GROUP BY StudentID
Normalization
Your table is in serious need of normalization. If you can change it, bare minimal change would look like this:
CREATE TABLE dbo.NormalizedTable
(
StudentID INT,
AssessmentCode CHAR(5),
ResultGroup VARCHAR(25),
ResultValue SMALLINT, --smallint range should be plenty. It can store values from -32,768 to 32,767
IBLevel CHAR(2)
)
INSERT INTO dbo.NormalizedTable
SELECT *
FROM CTE_Normalized
If you can't change the way your data is structured, I'd recommend creating a view from my CTE_normalized
CREATE VIEW vw_normalizedTable
AS
WITH CTE_ResultValue
AS
(
SELECT StudentID,
AssessmentCode,
ResultGroup,
CAST(Result AS INT) AS ResultValue
FROM #yourTable
WHERE ISNUMERIC(Result) = 1
),
CTE_IBLevel
AS
(
SELECT StudentID,
AssessmentCode,
Result AS IBLevel
FROM #yourTable
WHERE ISNUMERIC(Result) = 0
),
SELECT A.StudentID,
A.AssessmentCode,
A.ResultGroup,
A.ResultValue,
B.IBLevel
FROM CTE_ResultValue AS A
INNER JOIN CTE_IBLevel AS B
ON A.StudentID = B.StudentID
AND A.AssessmentCode = B.AssessmentCode
GO
Version1:
SELECT t1.StudentID,
SUM(CASE WHEN t1.Result = 'HL' THEN t2.Result ELSE 0 END) HLResult,
SUM(CASE WHEN t1.Result = 'SL' THEN t2.Result ELSE 0 END) SLResult,
SUM(CAST(t2.Result AS INT)) TotalResult,
SUM(CASE WHEN t1.Result = 'HL' THEN 1 ELSE 0 END) CountofHL,
SUM(CASE WHEN t1.Result = 'SL' THEN 1 ELSE 0 END) CountofSL,
CASE WHEN SUM(CAST(t2.Result AS INT)) > 24 THEN 'True' ELSE 'False' END MoreThan24
FROM #t t1
JOIN #t t2 ON t1.StudentID = t2.StudentID AND
t1.AssessmentCode = t2.AssessmentCode AND
t1.ResultGroup = 'IBLevel' AND
t2.ResultGroup = 'Mark'
GROUP BY t1.StudentID
Version2:
WITH cte1 AS(
SELECT *, ROW_NUMBER() OVER(PARTITION BY StudentID, AssessmentCode
ORDER BY CASE WHEN ResultGroup = 'IBLevel' THEN 1 ELSE 2 END) AS rn FROM #t),
cte2 AS(SELECT StudentID,
AssessmentCode,
SUM(CASE WHEN Result = 'HL' THEN 1 ELSE 0 END) HL,
SUM(CASE WHEN Result = 'SL' THEN 1 ELSE 0 END) SL,
MAX(CASE WHEN rn = 2 THEN Result END) R
FROM cte1
GROUP BY StudentID, AssessmentCode)
SELECT StudentID,
SUM(hl*R) HLResult,
SUM(sl*R) SLResult,
SUM((hl + sl)*r) TotalResult,
SUM(hl) CountofHL,
SUM(sl) CountofSL,
CASE WHEN SUM((hl + sl)*r) > 24 THEN 'True' ELSE 'False' END MoreThan24
FROM cte2
GROUP BY StudentID
Output:
StudentID HLResult SLResult TotalResult CountofHL CountofSL MoreThan24
46933 15 14 29 3 3 True
67767 4 5 9 1 1 False
Just replace #t with your table.

Cumulative sum with conditions

I tried to get cumulative sums throught column week (and if is possible, then get result ordered by column Datum$ADTE) in my query:
select
A.Castka "Amount",
'Cashflow' as N,
A.Datum$DATE "Datum",
cast(A.Rok as varchar(4)) + '-' + cast(A.Tyden as varchar(4)) "Week"
from iGateCashflow A
where
1 = 1
and A.BusTransaction_ID = '1D00000101'
and A.Vyjasneno = 'A'
and cast(datepart(week ,convert(date, dbo.ib_DateToString(Napocteno$DATE, 'dd.mm.yyyy'), 104)) as varchar(4))
= 44
In where clause are conditions, whichs will be edited througt external form.
I would like to get result like this (better without Amount column)
week N Amount Result
44 Cashflow 150 150
45 Cashflow 200 350
46 Cashflow 300 650
47 Cashflow 350 1000
I tried something like this, but I can't achieve my expected result:
select
sum(y.Amount),
Y.N,
Y.Week
from (
select
A.Castka "Amount",
'Cashflow' as N,
A.Datum$DATE "Datum",
cast(A.Rok as varchar(4)) + '-' + cast(A.Tyden as varchar(4)) "Week"
from iGateCashflow A
where
1 = 1
and A.BusTransaction_ID = '1D00000101'
and A.Vyjasneno = 'A'
and cast(datepart(week ,convert(date, dbo.ib_DateToString(Napocteno$DATE, 'dd.mm.yyyy'), 104)) as varchar(4))
= 44
) X
join (
select
A.Castka "Amount",
'Cashflow' as N,
cast(A.Rok as varchar(4)) + '-' + cast(A.Tyden as varchar(4)) "Week"
from iGateCashflow A
where
1 = 1
and A.BusTransaction_ID = '1D00000101'
and A.Vyjasneno = 'A'
and cast(datepart(week ,convert(date, dbo.ib_DateToString(Napocteno$DATE, 'dd.mm.yyyy'), 104)) as varchar(4))
= 44
) Y on Y.Week <= X.Week
group by
Y.N,
Y.Week
order by
Y.Week
I think there's more your question than what you showed. But use this as an idea (you'll need to add in WHERE conditions, etc)
SELECT *,
SUM(Amount) OVER (ORDER BY [week])
FROM iGateCashflow
Better to use OVER Clause to get Sum partition by Week and order by Date, please refer to
Over Clause
Maybe try something like this
select
cast(A.Rok as varchar(4)) + '-' + cast(A.Tyden as varchar(4)) "Week",
sum(B.Castka) "Total amount"
from iGateCashflow A
inner join iGateCashflow B
on A.BusTransaction_ID = B.BusTransaction_ID
and A.Vyjasneno = B.Vyjasneno
and B.week <= A.week --> if week is a calculated field, include the calculation here for both 'week's
where
1 = 1
and A.BusTransaction_ID = '1D00000101'
and A.Vyjasneno = 'A'
and cast(datepart(week ,convert(date, dbo.ib_DateToString(Napocteno$DATE, 'dd.mm.yyyy'), 104)) as varchar(4))
= 44
group by cast(A.Rok as varchar(4)) + '-' + cast(A.Tyden as varchar(4)) "Week"

How to generate larger sets of lottery numbers efficiently

I am a beginner with SQL and I was looking for more experiences with SQL hence I decided to design a procedure to generate X amount of random lotto picks. The lottery here in my area allows you to pick 5 numbers from 1-47 and 1 "mega" number from 1-27. The trick is the "mega" number could repeat with the 5 numbers previously, i.e. 1, 2, 3, 4, 5, mega 1.
I created the following procedure to generate 10 million lottery picks, and it took 12 hours and 57 minutes for the process to finish. While my friends tested the same thing with java and it took seconds. I was wondering if there's any improvements I can make to the code or if there's any mistakes that I've made? I'm new at this hence I am trying to learn better approaches etc, all comments welcome.
USE lotto
DECLARE
#counter INT,
#counter1 INT,
#pm SMALLINT,
#i1 SMALLINT,
#i2 SMALLINT,
#i3 SMALLINT,
#i4 SMALLINT,
#i5 SMALLINT,
#sort int
SET #counter1=0
TRUNCATE TABLE picks
WHILE #counter1<10000000
BEGIN
TRUNCATE TABLE sort
SET #counter = 1
WHILE #counter < 6
BEGIN
INSERT INTO sort (pick)
SELECT CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT)
IF (SELECT count(distinct pick) FROM sort)<#counter
BEGIN
TRUNCATE TABLE sort
SET #counter=1
END
ELSE IF (SELECT COUNT(DISTINCT pick) FROM sort)=#counter
BEGIN
SET #counter = #counter + 1
END
END
SET #sort = 0
WHILE #sort<5
BEGIN
UPDATE sort
SET sort=#sort
WHERE pick = (SELECT min(pick) FROM sort WHERE sort is null)
SET #sort=#sort + 1
END
SET #i1 = (SELECT pick FROM sort WHERE sort = 0)
SET #i2 = (SELECT pick FROM sort WHERE sort = 1)
SET #i3 = (SELECT pick FROM sort WHERE sort = 2)
SET #i4 = (SELECT pick FROM sort WHERE sort = 3)
SET #i5 = (SELECT pick FROM sort WHERE sort = 4)
SET #pm = (CAST(((27+ 1) - 0) * RAND() + 1 AS TINYINT))
INSERT INTO picks(
First,
Second,
Third,
Fourth,
Fifth,
Mega,
Sequence
)
Values(
#i1,
#i2,
#i3,
#i4,
#i5,
#pm,
#counter1
)
SET #counter1 = #counter1+1
END
I generated 10000 rows in 0 sec. I did it i another way. Hope this will help you
;WITH Nbrs ( n ) AS (
SELECT 1 UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 10000 )
SELECT
(ABS(CHECKSUM(NewId())) % 47 + 1) AS First,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Second,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Third,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Fourth,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Fifth,
(ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega,
Nbrs.n AS Sequence
FROM
Nbrs
OPTION ( MAXRECURSION 0 )
10000 rows 0 sec
100000 rows 1 sec
1000000 rows 13 sec
10000000 rows 02 min 21 sec
Or with cross joins
WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
E02(N) AS (SELECT 1 FROM E00 a, E00 b),
E04(N) AS (SELECT 1 FROM E02 a, E02 b),
E08(N) AS (SELECT 1 FROM E04 a, E04 b),
E16(N) AS (SELECT 1 FROM E08 a, E08 b),
E32(N) AS (SELECT 1 FROM E16 a, E16 b),
Nbrs(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
SELECT
(ABS(CHECKSUM(NewId())) % 47 + 1) AS First,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Second,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Third,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Fourth,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Fifth,
(ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega,
Nbrs.n AS Sequence
FROM Nbrs
WHERE N <= 10000000;
10000 rows 0 sec
100000 rows 1 sec
1000000 rows 14 sec
10000000 rows 03 min 29 sec
I should also mention that the reason I am using
(ABS(CHECKSUM(NewId())) % 47 + 1)
is that it returns a random number per row. The solution with
CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT)
return the same random number for each row if you select them in one go. To test this run this example:
;WITH Nbrs ( n ) AS (
SELECT 1 UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 5 )
SELECT
CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT) AS Random,
(ABS(CHECKSUM(NewId())) % 47 + 1) AS RadomCheckSum,
Nbrs.n AS Sequence
FROM Nbrs
Ok. So I did see your comment and I have a solution for that as well. If you really want to order the numbers. The complexity of the algorithm elevates and that also means that the time of the algorithm increases. But i still think it is doable. But not in the same neat way.
--Yeah declaring a temp table for just the random order number
DECLARE #tbl TABLE(value int)
--The same function but with the number of the random numbers
;WITH Nbrs ( n ) AS (
SELECT 1 UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 5 )
INSERT INTO #tbl
(
value
)
SELECT
Nbrs.n AS Sequence
FROM Nbrs
;WITH Nbrs ( n ) AS (
SELECT CAST(1 as BIGINT) UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 100000 )
SELECT
tblOrderRandomNumbers.[1] AS First,
tblOrderRandomNumbers.[2] AS Second,
tblOrderRandomNumbers.[3] AS Third,
tblOrderRandomNumbers.[4] AS Fourth,
tblOrderRandomNumbers.[5] AS Fifth,
(ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega,
Nbrs.n AS Sequence
FROM
Nbrs
--This cross join. Joins with the declared table
CROSS JOIN
(
SELECT
[1], [2], [3], [4], [5]
FROM
(
SELECT
Random,
ROW_NUMBER() OVER(ORDER BY tblRandom.Random ASC) AS RowNumber
FROM
(
SELECT
(ABS(CHECKSUM(NewId())) % 47 + 1) AS Random
FROM
#tbl AS tblNumbers
) AS tblRandom
)AS tblSortedRadom
--A pivot makes the rows to columns. Using the row index over order of the random number
PIVOT
(
AVG(Random)
FOR RowNumber IN ([1], [2], [3], [4],[5])
) as pivottable
) AS tblOrderRandomNumbers
OPTION ( MAXRECURSION 0 )
But still i manage to do it in a little time
10000 Rows : 0 sec
100000 Rows : 4 sec
1000000 Rows : 43 sec
10000000 Rows : 7 min 9 sec
I Hope this help
I wrote this script just out of curiousity. It should do better than your script, but I cant tell for sure.
Beware that I use a declared table, and if you use a real table performance should be better when generating larger amounts of rows.
I generated 10000 rows on about 13 seconds, that counts to about 3.5 hours to generate 10 000 000 rows. Still far worse than the Java-case you described.
set nocount on
go
declare #i int = 1
declare #t table(nr1 int, nr2 int, nr3 int, nr4 int, nr5 int, mega int, seq int)
while #i <= 10000
begin
;with numbers(nr)
as
(
select 1
union all
select nr+1
from numbers
where nr < 47
)
,mega(nr)
as
(
select 1
union all
select nr+1
from mega
where nr < 27
)
,selectednumbers(nr)
as
(
select top 5 nr
from numbers
order by newid()
)
,selectedmega(mega)
as
(
select top 1 nr
from mega
order by newid()
)
,tmp
as
(
select *
,row_number() over(order by nr) as rownr
from selectednumbers
)
insert into #t
select max(nr1) as nr1
,max(nr2) as nr2
,max(nr3) as nr3
,max(nr4) as nr4
,max(nr5) as nr5
,(select mega from selectedmega) as mega
,#i as seq
from (
select case when rownr = 1 then nr else 0 end as nr1
,case when rownr = 2 then nr else 0 end as nr2
,case when rownr = 3 then nr else 0 end as nr3
,case when rownr = 4 then nr else 0 end as nr4
,case when rownr = 5 then nr else 0 end as nr5
from tmp
) x
set #i = #i + 1
end
select * from #t

T-SQL A problem with SELECT TOP (case [...])

I have query like that:
(as You see I'd like to retrieve 50% of total rows or first 100 rows etc)
//#AllRowsSelectType is INT
SELECT TOP (
case #AllRowsSelectType
when 1 then 100 PERCENT
when 2 then 50 PERCENT
when 3 then 25 PERCENT
when 4 then 33 PERCENT
when 5 then 50
when 6 then 100
when 7 then 200
end
) ROW_NUMBER() OVER(ORDER BY [id]) AS row_num, a,b,c etc
why have I the error : "Incorrect syntax near the keyword 'PERCENT'." on line "when 1 [...]"
The syntax for TOP is:
TOP (expression) [PERCENT]
[ WITH TIES ]
The reserved keyword PERCENT cannot be included in the expression. Instead you can run two different queries: one for when you want PERCENT and another for when you don't.
If you need this to be one query you can run both queries and use UNION ALL to combine the results:
SELECT TOP (
CASE #AllRowsSelectType
WHEN 1 THEN 100
WHEN 2 THEN 50
WHEN 3 THEN 25
WHEN 4 THEN 33
ELSE 0
END) PERCENT
ROW_NUMBER() OVER(ORDER BY [id]) AS row_num, a, b, c, ...
UNION ALL
SELECT TOP (
CASE #AllRowsSelectType
WHEN 5 THEN 50
WHEN 6 THEN 100
WHEN 7 THEN 200
ELSE 0
END)
ROW_NUMBER() OVER(ORDER BY [id]) AS row_num, a, b, c, ...
You're also mixing two different types of use. The other is.
DECLARE #ROW_LIMT int
IF #AllRowsSelectType < 5
SELECT #ROW_LIMIT = COUNT(*)/#AllRowsSelectType FROM myTable -- 100%, 50%, 33%, 25%
ELSE
SELECT #ROW_LIMIT = 50 * POWER(2, #AllRowsSelectType - 5) -- 50, 100, 200...
WITH OrderedMyTable
(
select *, ROW_NUMBER() OVER (ORDER BY id) as rowNum
FROM myTable
)
SELECT * FROM OrderedMyTable
WHERE rowNum <= #ROW_LIMIT
You could do:
select top (CASE #FilterType WHEN 2 THEN 50 WHEN 3 THEN 25 WHEN 4 THEN 33 ELSE 100 END) percent * from
(select top (CASE #FilterType WHEN 5 THEN 50 WHEN 6 THEN 100 WHEN 7 THEN 200 ELSE 2147483647 END) * from
<your query here>
) t
) t
Which may be easier to read.