I have three variables :-
#ScoreA DECIMAL(10,7)
#ScoreB DECIMAL(10,7)
#ScoreC DECIMAL(10,7)
#FinalScore DECIMAL(10, 7)
I wish to get the average of the three scores. BUT 1, 2 or all 3 values might be zero.
Eg. scenarios:
A = 1.4, B=3.5, C=5.0; FinalScore = 3.3
A = 0.0, B=0.0, C=0.0; FinalScore = 0.0
A = 1.1, B=0.0, C=0.0; FinalScore = 1.1
A = 0.0, B=2.0, C=4.8; FinalScore = 3.4
Cheers!
IF #A > 0 OR #B > 0 OR #C > 0
SELECT ((#A + #B + #C) /
(0 +
CASE WHEN #A = 0 THEN 0 ELSE 1 END +
CASE WHEN #B = 0 THEN 0 ELSE 1 END +
CASE WHEN #C = 0 THEN 0 ELSE 1 END ))
ELSE
SELECT 0.0
EDIT
Modified query to now handle divide by zero scenario's.
EDIT2
Here is "the trick with the AVG(..) function" :) with Common Table Expression
WITH T(I) AS (SELECT #A UNION SELECT #B UNION SELECT #C)
SELECT AVG(I) FROM T
WHERE I > 0
SELECT ((#A + #B + #C) /
(CASE WHEN (#A = 0.0 AND #B = 0.0 AND #C = 0.0) THEN 1 ELSE 0 END
+ CASE WHEN #A = 0 THEN 0 ELSE 1 END
+ CASE WHEN #B = 0 THEN 0 ELSE 1 END
+ CASE WHEN #C = 0 THEN 0 ELSE 1 END
)
)
For me this is easier to read and understand:
DECLARE
#ScoreA DECIMAL(10,7),
#ScoreB DECIMAL(10,7),
#ScoreC DECIMAL(10,7),
#FinalScore DECIMAL(10, 7)
SET #ScoreA = 1.4
SET #ScoreB = 3.5
SET #ScoreC = 5.0
DECLARE
#AVG TABLE (value DECIMAL(10,7))
INSERT INTO #AVG
SELECT #ScoreA WHERE #ScoreA > 0
UNION
SELECT #ScoreB WHERE #ScoreB > 0
UNION
SELECT #ScoreC WHERE #ScoreC > 0
SELECT COALESCE(AVG(value), 0) FROM #AVG
Related
i want to get sum of pivoted column values to new column and insert output records to a temporary table
select * from (select v.JobNo as JobNo,aj.VehicleNumber as VehicleNo,isnull(g.ImageCount,0) as ImageCount,s.ParamKey as ImageType from dbo.Visits v inner join (select VisitId as visit,paramkey,Value from dbo.VisitParams) s on s.visit = v.visitid left outer join ( select VisitId,FieldId, ( COUNT(*)) as ImageCount from dbo.vw_ImageGallery group by FieldId,VisitId) g on s.visit = g.VisitId and g.FieldId = s.ParamKey inner join Users u on u.UserId = v.CreatedBy inner join AssignedJobs aj on aj.CSRCode = u.Code and aj.JobNumber = v.JobNo where v.VisitType = 1 and v.TimeVisited >= '2019-03-01' AND v.TimeVisited <= '2019-04-01' )as a
PIVOT ( max([ImageCount]) FOR [ImageType] IN ([5],[20],[21]) ) as pvt order by [JobNo]
my actual out put is
job no vehicleno 1 2 5
---------------------------------------------------------
BL1052385 648792 0 8 0
BL1054161 CAT2410 2 8 0
BL1107290 NB 0134 0 5 0
BL1174714 GP 3784 1 7 3
i expect the output like
job no vehicleno 1 2 5 Total Count
----------------------------------------------------------
BL1052385 648792 0 8 0 8
BL1054161 CAT2410 2 8 0 10
BL1107290 NB 0134 0 5 0 5
BL1174714 GP 3784 1 7 3 11
I prefer using conditional aggregation rather than pivot. It is more flexible:
select v.JobNo, aj.VehicleNumber,
sum(case when vp.ParamKey = 1 then g.ImageCount else 0 end) as imagetype_1,
sum(case when vp.ParamKey = 20 then g.ImageCount else 0 end) as imagetype_20,
sum(case when vp.ParamKey = 21 then g.ImageCount else 0 end) as imagetype_21,
count(*) as total
from dbo.Visits v join
dbo.VisitParams vp
on vp.visit = v.visitid join
Users u
on u.UserId = v.CreatedBy join
AssignedJobs aj
on aj.CSRCode = u.Code and
aj.JobNumber = v.JobNo left outer join
(select VisitId, FieldId, count(*) as ImageCount
from dbo.vw_ImageGallery ig
group by FieldId, VisitId
) g
on vp.visit = g.VisitId and
g.FieldId = s.ParamKey
where v.VisitType = 1 and
v.TimeVisited >= '2019-03-01' and
v.TimeVisited <= '2019-04-01' and
vp.ParamKey in (5, 20, 21)
group by v.JobNo, aj.VehicleNumber;
order by v.JobNo
I have the following clause in my legacy code that we need to move over to our new EF solution. It is used for doing currency conversions.
SELECT TOP 25 job_id, j.salary_format_id, j.currency_id, j.offer_date, j.accepted_date, j.reported_date, j.start_date,
CASE
WHEN salary_format_id = 1 AND salary > 0 THEN salary * 1
WHEN salary_format_id = 2 AND salary > 0 THEN salary * 4
WHEN salary_format_id = 3 AND salary > 0 THEN salary * 12
WHEN salary_format_id = 4 AND salary > 0 THEN salary * 52
WHEN salary_format_id = 5 AND salary > 0 THEN salary * 5 * 52
WHEN salary_format_id = 6 AND salary > 0 THEN salary * 40 * 52
END * (c2.rate / c1.rate) AS salary
FROM job j
LEFT JOIN exchange_rate c1 ON CAST(
(
CASE
WHEN COALESCE(j.offer_date, j.accepted_date, j.reported_date, j.start_date) < '1999-01-01' THEN '1999-01-01'
ELSE COALESCE(j.offer_date, j.accepted_date, j.reported_date, j.start_date)
END
) AS Date) = c1.date AND c1.currency_id = j.currency_id
LEFT JOIN exchange_rate c2 ON CAST(
(
CASE
WHEN COALESCE(j.offer_date, j.accepted_date, j.reported_date, j.start_date) < '1999-01-01' THEN '1999-01-01'
ELSE COALESCE(j.offer_date, j.accepted_date, j.reported_date, j.start_date)
END
) AS Date) = c2.date AND c2.currency_id = #currency_id
My intent here is to convert currency based on the date (we have the rates stored in the 'exchnage_rate' table. It uses the COALESCE to prioritize which date column to use in order to get the proper rate.
I have the following line (reduced for simplicity):
var baseQuery = this.dbContext.Jobs;
...
How do I convert the following SQL into Entity Framework code here?
I've been banging my head against the wall a while.
I have a table that has DwgID (integer) and Current (boolean) as part of the table.
What I want to do is filter just the DwgID's that have more than 1 Current selected.
This code gives me the count correctly:
`SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "Current" FROM "tblRev" GROUP BY "DwgID"`
But if I try to add a criteria where the Current Count is > 1, it fails with a data content could not be loaded.
`SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "Current" FROM "tblRev" GROUP BY "DwgID" HAVING ( ( SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) > 1 ) )`
What am I doing wrong?
You need to choose a different alias because Current is the column name. Let's call it NumCurrent instead.
SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "NumCurrent" FROM "tblRev" GROUP BY "DwgID" HAVING ( ( SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) > 1 ) )
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
I have a table of items that are split on a siteId and a status.
CREATE TABLE ItemDetail (
ItemNumber long,
SiteId int,
Status int,
Created datetime
)
And then I also have a user's table
CREATE TABLE UserDetail (
UserId int,
Suspended int
)
Status has 7 possible values (0-6) representing different queues. Let's call this variable x. Suspended, should only have 0 (active) or 1 (suspended). (don't ask me why it's an int, I didn't build it). Data is shown per site in a configurable time frame. Default is 5 days back. Let's call this variable a. I want to return, in one call, a dataset like this:
ActiveUserCount int
SuspendedUserCount int,
Queue0Count int,
Queue0TodayCount int,
Queue1Count int,
Queue1TodayCount int,
...
Where QueueXCount is everything in the last a days by status and siteid. QueueXTodayCount is everything with status x that happened today. So far, I have started a sproc like this
CREATE PROCEDURE GetSiteStatistics
#SiteId int,
#Window int
AS
BEGIN
DECLARE #Today datetime
DECLARE #Tomorrow datetime
DECLARE #CutOff datetime
SET #Today = (CAST(YEAR(getdate()) as varchar) +
RIGHT('00' + CAST(MONTH(getdate()) as varchar), 2) +
RIGHT('00' + CAST(DAY(getdate()) as varchar), 2))
SET #Tomorrow = DATEADD(dd, 1, #Today)
SET #CutOff = DATEADD(dd, #Window + 1, #Today)
DECLARE
#SuspendedUserCount int,
#ActiveUserCount int,
#Queue0Count int,
#Queue0TodayCount int,
...
SELECT #SuspendedUserCount = count(UserId) FROM UserDetail WHERE Suspended = 1 AND SiteId = #SiteId
SELECT #ActiveUserCount = count(UserId) FROM UserDetail WHERE Suspended = 0 AND SiteId = #SiteId
SELECT #Queue0Count = count(ItemNumber) FROM ItemDetail WHERE Status = 0 AND SiteId = #SiteId AND Created >= #Today AND Created < #CutOff
SELECT #Queue0TodayCount = count(ItemNumber) FROM ItemDetail WHERE Status = 0 AND SiteId = #SiteId AND Created >= #Today AND Created < #Tomorrow
...
SELECT #SuspendedUserCount AS SuspendedUsers, #ActiveUserCount AS ActiveUsers, #Queue0Count AS Queue0, #Queue0TodayCount AS #Queue0Today...
END
As if that isn't complicated enough, I am using Fluent Nhibernate. I'm not against using a sproc if I have to, but I have considered creating a view with the counts by day, pulling that in using nhibernate and a query to pick by date range, and then summing the amounts where appropriate in code.
I just have a feeling I am making this more complicated than I have to. There must be a better way.
Perhaps you can use this.
declare #SiteId int = 1
declare #Window int = 5
declare #ToDay datetime = dateadd(d, datediff(d, 0, getdate()), 0)
declare #CutOff datetime = dateadd(d, #Window, #ToDay)
declare #Tomorrow datetime = dateadd(d, 1, #ToDay)
select * from
(
select
sum(case when Suspended = 0 then 1 else 0 end) as ActiveUserCount,
sum(case when Suspended = 1 then 1 else 0 end) as SuspendedUserCount
from UserDetail
where
SiteId = #SiteId
) as q1 cross join
(
select
sum(case when Status = 0 then 1 else 0 end) as Queue0Count,
sum(case when Status = 1 then 1 else 0 end) as Queue1Count,
sum(case when Status = 2 then 1 else 0 end) as Queue2Count,
sum(case when Status = 3 then 1 else 0 end) as Queue3Count,
sum(case when Status = 4 then 1 else 0 end) as Queue4Count,
sum(case when Status = 5 then 1 else 0 end) as Queue5Count,
sum(case when Status = 6 then 1 else 0 end) as Queue6Count,
sum(case when Status = 0 and Created < #Tomorrow then 1 else 0 end) as Queue0TodayCount,
sum(case when Status = 1 and Created < #Tomorrow then 1 else 0 end) as Queue1TodayCount,
sum(case when Status = 2 and Created < #Tomorrow then 1 else 0 end) as Queue2TodayCount,
sum(case when Status = 3 and Created < #Tomorrow then 1 else 0 end) as Queue3TodayCount,
sum(case when Status = 4 and Created < #Tomorrow then 1 else 0 end) as Queue4TodayCount,
sum(case when Status = 5 and Created < #Tomorrow then 1 else 0 end) as Queue5TodayCount,
sum(case when Status = 6 and Created < #Tomorrow then 1 else 0 end) as Queue6TodayCount
from ItemDetail
where
Created >= #ToDay and
Created < #CutOff
) as q2
I created a view using CTE and a pivot
CREATE VIEW [dbo].[SiteQueueDailyStatistics]
AS
WITH statCTE AS (
SELECT
Count(ItemNumber)as ItemCount,
SiteId,
Status,
DATEADD(dd, 0, DATEDIFF(dd, 0, ScanDate)) AS ScanDay
FROM
ItemDetail
group by SiteId, Status, DATEADD(dd, 0, DATEDIFF(dd, 0, ScanDate))
)
SELECT
SiteId,
ScanDay,
ISNULL([0], 0) AS Queue0,
ISNULL([1], 0) AS Queue1,
ISNULL([2], 0) AS Queue2,
ISNULL([3], 0) AS Queue3,
ISNULL([4], 0) AS Queue4,
ISNULL([5], 0) AS Queue5,
ISNULL([6], 0) AS Queue6
FROM
statCTE
PIVOT
(
SUM(ItemCount)
FOR [Status] IN ([0], [1], [2], [3], [4], [5], [6])
)
AS p
GO
And then I get all my data and populate a composite object in my model that holds the statistics across multiple tables.