How do I put condition in MAX in GROUP BY - sql

I want to group my data by Date and then find max "Value" for different "Codes". How can I do this - I would like to supply a condition to MAX() but I don't think that is possible.
Columns:
Date: date
Time: time
Value: float
Code: varchar
SELECT MAX([Value] where [Code]='GOLD') AS BestGold,
MAX([Value] where [Code]='SILVER') AS BestSilver
FROM [MyTable]
GROUP BY [Date]

Use a CASE expression:
SELECT
MAX(CASE WHEN [Code]='GOLD' THEN ['Value'] END) AS BestGold,
MAX(CASE WHEN [Code]='SILVER' THEN ['Value'] END) AS BestSilver
FROM [MyTable]
GROUP BY [Date];
The idea here is that the MAX function will only consider values of records for each respective type of code.

You can use inline iif:
SELECT MAX(IIF([Code]='GOLD', [Value], null)) AS BestGold,
MAX(IIF([Code]='SILVER', [Value], null)) as BestSilver
FROM [MyTable]
GROUP BY [Date]
or case:
SELECT MAX(case [Code] when 'GOLD' then [Value] end) AS BestGold,
MAX(case [Code] when 'SILVER' then [Value] end) as BestSilver
FROM [MyTable]
GROUP BY [Date]

Instead, use this
SELECT
[Date],
[Code],
MAX([Value]) AS Best
FROM [MyTable]
where cODE IN ('GOLD','SILVER')
GROUP BY [Date]
If you want it as Separate Column, Then try Pivoting the same
;WITH CTE
AS
(
SELECT
[Date],
[Code],
MAX([Value]) AS Best
FROM [MyTable]
where cODE IN ('GOLD','SILVER')
GROUP BY [Date]
)
SELECT
[Date],
BestGold = [GOLD],
BestSilver = [Silver]
FROM CTE
PIVOT
(
MAX(Best)
FOR
Code IN
(
[GOLD],[SILVER]
)
)P

You could use case;
SELECT
MAX(CASE WHEN Code='GOLD' THEN [VALUE] ELSE -1 END) AS BestGold,
MAX(CASE WHEN Code='SILVER' THEN [VALUE] ELSE -1 END) AS BestSilver
FROM [MyTable]
GROUP BY [Date];

I believe you require something like the below :-
DECLARE #TestCodes Table
(
Date date,
Time time,
Value float,
Code varchar(10)
)
INSERT INTO #TestCodes
VALUES
('2017-08-09','12:00',19900,'Gold'),
('2017-08-09','12:00',15001,'Gold'),
('2017-08-09','12:00',2500,'Gold'),
('2017-08-09','12:00',1200.01,'Metal'),
('2017-08-09','12:00',1900,'Metal'),
('2017-08-09','12:00',1800.1,'Silver'),
('2017-08-09','12:00',1100.01,'Silver'),
('2017-08-09','12:00',100.11,'Silver')
SELECT Date,Code,Max(value) AS MAXPriceOnAnyDate
FROM #TestCodes
GROUP BY [Date],CODE

Related

SQL case statement closest to current date

Need help create a case statement to find the closest date from date table. My data: https://imgur.com/hkBu4SA
I basically want to set:
Y flag if it's closest to today's date from a.FROM_EFFDT and is not null.
F if to_effdate is null
else N
WHEN a.FROM_EFFDT < GETDATE() AND (to_effdate) IS NOT NULL THEN 'Y'
WHEN to_effdate IS NULL THEN 'F'
ELSE 'N'
You can use window functions:
(case when row_number() over (order by abs(datediff(day, getdate(), to_effdate)) = 1
then 'Y'
when to_effdate is null then 'F'
else 'N'
end)
You may be able to accomplish it with something like this. Though this isn't bulletproof, you could get duplicates if the closest date is tied.
create table Dates (from_effdt datetime, to_effdt datetime, flag varchar(1))
insert Dates (from_effdt, to_effdt, flag)
values
('2019-03-16', null, '') ,
('2018-06-14', '2019-03-16', '') ,
('2018-05-14', '2018-06-14', '') ,
('2018-01-01', '2018-05-14', '')
select * from Dates
UPDATE Dates
SET flag =
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END
*update, not sure why I created it as an update. This select should do.
SELECT from_effdt, to_effdt,
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END [numberOfDaysAway]
FROM Dates
You can simply do this:
CASE
WHEN from_effdt = (
select from_effdt
from Dates
where abs(datediff(second, from_effdt, getdate()))
= (select min(
abs(datediff(second, from_effdt, getdate()))
)
from Dates)
)
THEN 'Y'
ELSE
'N'
END
ROW_NUMBER() Over (Partition by id order by to_effdt desc)
,id
,from_effdt
,to_effdt
, CASE WHEN (ROW_NUMBER() Over (Partition by id order by to_effdt desc) = 1) THEN ('Y')
WHEN (to_effdt IS NULL) THEN ('F') ELSE ('N') End as flag
from a

SQL: Cast Not working in SubString

We have a string field for date of birth and now we have to convert it in order to perform the calculations required. However when we are using CAST or CONVERT to convert to perform the calculations it is not working.
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
PatientView
where ISDATE(DOB) = 1
)
SQL Server considers this a "feature". It is hard to explain, but the where is not necessarily executed before the select.
In SQL Server 2012+, use try_convert() (or try_cast():
where ptr.CHName like 'Access2Loc%' and
ptr.RecordID in (select (case when DATEDIFF(hour, try_convert(date, DOB, 110), GETDATE()) / 8766 > 18
then PatientID
end) as RecordID
from PatientView
where ISDATE(DOB) = 1
)
In more ancient versions, you can use a case expression for much the same effect.
-- this will guarantee that ##t1 contains only valid dob rows
if object_id('tempdb..##t1') is not null
drop table #t1
select * into ##t1 from patientview where isdate(dob)=1
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
##t1
)
drop table ##t1

How to count events based on parsing date - GROUP BY issues

I am attempting to count how many FAILURE events occurred per day, and the events are stored in a MainEventTable with columns of EventDateTime, EventId, and EventStatus. I'm using SQL Server Management Studio 2016, and it didn't recognize the DATEFROMPARTS function. This is the code that I've put together so far:
SELECT
t.EventDate,
SUM(t.EventCount) AS EventCount
FROM (
SELECT
CAST(
(
CAST(
DATEPART(yyyy,s.EventDateTime)
AS VARCHAR(4)
) + '-' +
CAST(
DATEPART(mm,s.EventDateTime)
AS VARCHAR(2)
) + '-' +
CAST(
DATEPART(dd,s.EventDateTime)
AS VARCHAR(2)
)
) AS DATE
) AS EventDate,
Count(s.EventId) AS EventCount
FROM (
SELECT
EventDateTime,
EventId
FROM
MainEventTable WITH(NOLOCK)
WHERE EventDateTime > '2016-12-07 00:00:00'
AND EventStatus = 'FAILURE'
) AS s GROUP BY CAST(
(
CAST(
DATEPART(yyyy,s.EventDateTime)
AS VARCHAR(4)
) + '-' +
CAST(
DATEPART(mm,s.EventDateTime)
AS VARCHAR(2)
) + '-' +
CAST(
DATEPART(dd,s.EventDateTime)
AS VARCHAR(2)
)
) AS VARCHAR(10)
)
) AS t
GROUP BY t.EventDate;
UPDATE: (THANKS to #wrslphil and #PM_77-1 for assistance with my GROUP BY issues) I've fixed my GROUP BY issues above and found that this worked, although it was very clunky. #KeithL simplified it MUCH more below...
Not a lot to go on here, but the query definitely won't run if you have an item that isn't in the group by list or aggregated in your select clause
I would think that you probably need to do a sum of your count field... like so
SELECT
t.EventDate,
SUM(t.EventCount) as EventCount
FROM (
SELECT
CAST(
(
CAST(
DATEPART(yyyy,s.EventDateTime)
AS VARCHAR(4)
) +
CAST(
DATEPART(mm,s.EventDateTime)
AS VARCHAR(2)
) +
CAST(
DATEPART(dd,s.EventDateTime)
AS VARCHAR(2)
)
) AS DATE
) AS EventDate,
Count(s.EventId) As EventCount
FROM (
SELECT
EventDateTime,
EventId
FROM
MainEventTable WITH(NOLOCK)
WHERE EventDateTime > '2016-12-07 00:00:00'
AND EventStatus = 'FAILURE'
) AS s
) AS t
GROUP BY t.EventDate;
The query is still unlikely to run because you have the same issue in your inner query. I would probably just do the transformation of the date within an inner query and encapsulate it with a query containing a count and group by
select CAST(EventDateTime AS DATE),COUNT(*)
FROM MainEventTable
WHERE EventDateTime > '2016-12-07 00:00:00'
AND EventStatus = 'FAILURE'
GROUP BY CAST(EventDateTime AS DATE)
Your sub-query has the following structure:
SELECT field1, COUNT(field2)
FROM theTable
In the same select you use a "raw" field and aggregation (COUNT). You do not have GROUP BY clause.
You are confusing your SQL engine, since it's impossible to figure out what value of field1 should be picked.
If you intention was to count how many records with non-NULL value of field2 each value of field1 has then the code would be:
SELECT field1, COUNT(field2)
FROM theTable
GROUP BY field1

Iterate value dynamically

I'm using the below query to calculate a budget value dynamically means iterating upto selected date value.
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinMonth) end) as [Budget]
Here variable #DaysPastinMonth should be dynamic. Means if I select a date as 03/31/2017. Then the query should run upto the previous month value. Another example is if I select August, then I need to run query from Jan-Aug.
For Jan
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinJanMonth) end) as [Budget]
For Feb
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinFebMonth) end) as [Budget]
For Mar
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinMarMonth) end) as [Budget]
Also I have created variables for all the 12 months which holds DaysPastinMonth.
Can anyone suggest how this can be achieved using case statement.
You are thinking about this in loop when you could do it with set based operations.
----------------------------------------------------------
--Create a table of dates for testing
----------------------------------------------------------
if object_id('tempdb..#dates') is not null
drop table #dates
create table #dates(d date
,RN bigint)
declare #sdate datetime='2017-01-01 00:00'
declare #edate datetime='2017-7-31 00:00'
insert into #dates
select
DATEADD(d,number,#sdate)
,row_number() over (order by (select null)) as RN
from
master..spt_values
where
type='P'
and number<=datediff(d,#sdate,#edate)
declare #numOfDays int = (select count(*) from #dates)
----------------------------------------------------------
--Populate Test Data
----------------------------------------------------------
if object_id('tempdb..#testTable') is not null
drop table #testTable
create table #testTable([Name] varchar(64),
[Value] decimal (16,4),
DT datetime)
insert into #testTable ([Name],[Value],DT)
select
'Budget'
,r.randomNumber
,d.d
from
#dates d
inner join
(SELECT TOP (select #numOfDays)
randomNumber,
row_number() over (order by (select null)) as RN
FROM (
SELECT CAST(ABS(CAST(NEWID() AS binary(6)) %100000) + RAND() AS DECIMAL (16,4)) + 1 randomNumber
FROM sysobjects) sample
GROUP BY randomNumber
ORDER BY randomNumber DESC) r on r.RN = d.RN
union all
select
'Not The Budget'
,r.randomNumber
,d.d
from
#dates d
inner join
(SELECT TOP (select #numOfDays)
randomNumber,
row_number() over (order by (select null)) as RN
FROM (
SELECT CAST(ABS(CAST(NEWID() AS binary(6)) %100000) + RAND() AS DECIMAL (16,4)) + 1 randomNumber
FROM sysobjects) sample
GROUP BY randomNumber
ORDER BY randomNumber DESC) r on r.RN = d.RN
----------------------------------------------------------
--Instead of making your variables "dynamic" which
--would likely consist of some loop, just pass in the
--month you care about and let SQL do the work
----------------------------------------------------------
declare #month datetime = '2016-03-31'
select
DT
,[Value]
,[Name]
,sum(case when [Name] = 'Budget'
then [Value] +
(([Value] / (DATEDIFF(day,DATEADD(month, DATEDIFF(month, 0, #month), 0),#month)))
*
(DATEDIFF(DAY,DATEADD(MONTH, DATEDIFF(MONTH, 0, #month)-1, 0),DATEADD(MONTH, DATEDIFF(MONTH, -1, #month)-1, -1)))) end) as Budget
from
#testTable
where
DT >= DATEADD(yy, DATEDIFF(yy, 0, #month), 0) --this is Jan 1 of the year associated with your vairable
group by
DT
,[Name]
,[Value]

Add an additional flag value to the SQL query based on consecutive day logic

I have a table with the following structure
Date Holiday Flag
12/23/2016 -1
12/24/2016 -1
12/25/2016 1
12/26/2016 1
12/27/2016 -1
I want to add an additional flag based derived from the two columns mentioned above as such
Date Holiday Flag Previous Flag
12/23/2016 -1 -1
12/24/2016 -1 -1
12/25/2016 1 -1
12/26/2016 1 1
12/27/2016 -1 -1
Basically, in the event that there's a holiday on two consecutive days (12/25/2016 and 12/26/2016), I want 'Previous Flag' to reflect that on the second day (12/26/2016) as 1
I'm using SQL Server 2008 to form the query but cant seem to figure out the logic.
What is the best way to approach this situation? Thank you in advance for your help, I'm new to programming. Any help will be appreciated.
With the help of a CTE and Row_Number()
Declare #YourTable table (Date date, [Holiday Flag] int)
Insert Into #YourTable values
('12/23/2016',-1),
('12/24/2016',-1),
('12/25/2016', 1),
('12/26/2016', 1),
('12/27/2016',-1)
;with cte as (
Select *
,RN = Row_Number() over (Order By Date)
From #YourTable
)
Select A.Date
,A.[Holiday Flag]
,[Previous Flag] = IsNull(B.[Holiday Flag],A.[Holiday Flag])
From cte A
Left Join cte B on (B.RN=A.RN-1)
Order By A.Date
Returns
Not sure I agree with the desired results. I show 12/27 previous flag as 1
This might work.
DECLARE #T TABLE (Date DATETIME,HolidayFlag INT)
INSERT INTO #T SELECT '12/23/2016',-1
INSERT INTO #T SELECT '12/24/2016',-1
INSERT INTO #T SELECT '12/25/2016',1
INSERT INTO #T SELECT '12/26/2016',1
INSERT INTO #T SELECT '12/27/2016',-1
SELECT
This.Date,
This.HolidayFlag,
LastHolidayFlag=CASE WHEN Last.Date IS NULL THEN -1 ELSE Last.HolidayFlag END
FROM
(
SELECT Date,HolidayFlag,RowNumber=ROW_NUMBER() OVER (ORDER BY Date) FROM #T
)AS This
LEFT OUTER JOIN
(
SELECT Date,HolidayFlag, RowNumber=ROW_NUMBER() OVER (ORDER BY Date) FROM #T
)AS Last ON Last.RowNumber=This.RowNumber-1
Not too complicated. Try this.
Declare #Table table (Date date, [Holiday Flag] int)
Insert Into #Table values
('12/23/2016',-1),
('12/24/2016',-1),
('12/25/2016',1),
('12/26/2016',1),
('12/27/2016',-1)
Select A.Date
,A.[Holiday Flag]
,[Previous Flag] = IsNull(B.[Holiday Flag],A.[Holiday Flag])
From #Table A
Left Join #Table B on (DateAdd(day,-1 , A.Date)=B.Date)
Order By A.Date
Since you have sequential dates, a ROW_NUMBER() is superfluous. Try this:
Declare #MyTbl table (
Dt date PRIMARY KEY,
HolidayFlag int
)
Insert Into #MyTbl
values
('12/23/2016',-1),
('12/24/2016',-1),
('12/25/2016', 1),
('12/26/2016', 1),
('12/27/2016',-1)
SELECT t1.Dt as [Date],
t1.HolidayFlag as [Holiday Flag],
CASE
WHEN t1.HolidayFlag = 1
THEN IsNull(t2.HolidayFlag, -1)
ELSE -1
END as [Previous Flag]
FROM #MyTbl t1
LEFT JOIN #MyTbl t2
ON t2.dt = dateadd(day, -1, t1.dt)