I feel confident this query is written correctly but I am continuing to have "Null" inserted into the expected column. My start and end date columns are datetime and contain date so I am unsure why this is not performing the datediff calculation and updating the column.
Am I doing something wrong that is easily overlooked?
DECLARE #X INT
UPDATE MyTable
SET #X = DATEDIFF(s, Start_Date, End_Date)
,Column1 = CASE
WHEN Start_Date <> NULL
AND End_Date <> NULL
THEN (
SELECT CONVERT(VARCHAR(10), (#x / 86400)) + ' Days ' + CONVERT(VARCHAR(10), ((#x % 86400) / 3600)) + ' Hours ' + CONVERT(VARCHAR(10), (((#x % 86400) % 3600) / 60)) + ' Minutes '
)
ELSE NULL
END
Sample Data:
Start Date = 2018-08-08 00:00:00.000
End Date = 2020-08-08 00:00:00.000
(This is the actual data I am using in my real DB)
You must drop the select statement inside case:
DECLARE #X INT
UPDATE MyTable
SET #X = DATEDIFF(s, Start_Date, End_Date)
,Column1 = CASE
WHEN Start_Date is not NULL AND End_Date is not NULL
THEN CONVERT(VARCHAR(10), (#x / 86400)) + ' Days ' +
CONVERT(VARCHAR(10), ((#x % 86400) / 3600)) + ' Hours ' +
CONVERT(VARCHAR(10), (((#x % 86400) % 3600) / 60)) + ' Minutes '
ELSE NULL
END
It seems that the subquery
(SELECT CONVERT....)
is evaluated before
#X = DATEDIFF(s, Start_Date, End_Date)
so #X is still null and the result of the subquery is null.
Yes. Please see Not equal <> != operator on NULL
<> is Standard SQL-92; != is its equivalent. Both evaluate for values, which NULL is not -- NULL is a placeholder to say there is the absence of a value.
Which is why you can only use IS NULL/IS NOT NULL as predicates for such situations.
Related
I have the following query to run, which takes 50-60ms (query plan here):
SELECT (SUM(added_vacancies) + (SELECT active_vacancies FROM report.vacancy_stats WHERE date = make_date(2021,06,01) AND company_id = 6886 LIMIT 1) - SUM(archived_vacancies)) as result
FROM report.vacancy_stats
WHERE company_id = 6886
AND vacancy_stats.date >= (make_date(2021,06,01) + integer '1')::DATE
AND vacancy_stats.date <= make_date(2021,09,25)
LIMIT 1;
Now I've put it into a function like this; and suddenly, it takes at least 300ms to run:
CREATE OR REPLACE FUNCTION report.cp_active_vacancy_count_during_period(cid integer, date_start date, date_end date)
RETURNS bigint
LANGUAGE sql
STABLE LEAKPROOF PARALLEL SAFE
AS $function$
SELECT (SUM(added_vacancies) + (SELECT active_vacancies FROM report.vacancy_stats WHERE date = date_start AND company_id = cid LIMIT 1) - SUM(archived_vacancies)) as result
FROM report.vacancy_stats
WHERE company_id = cid
AND vacancy_stats.date >= (date_start + integer '1')::DATE
AND vacancy_stats.date <= date_end
LIMIT 1;
$function$;
What could be the reason for this huge difference, and how can I solve that issue?
I am trying to declare a variable for the current year and month based on a field called YEARMO.
It's a 6 character varchar field with the first 4 characters representing the year and the next 2 characters representing the month (ex. February 2018 as 201802).
I need the month variable to have a 0 in front of it if is a month that only has one digit (any month before October). Current code returns '20182' and would like it to return '201802'.
declare #current_month varchar(6)
set #current_month = CAST( year(getdate()) as varchar(4))
+ case when len(CAST( month(getdate()) as varchar(2))) < 1 then '0' +
CAST( month(getdate()) as varchar(2))
else CAST( month(getdate()) as varchar(2))
end
select #current_month;
You can use Convert function if you are looking for current month
select convert(char(6), getdate(), 112)
Your code is over complicated. Use right instead:
declare #current_month char(6)
set #current_month = CAST( year(getdate()) as varchar(4)) + right('00' + cast(month(getdate()) as varchar(2)), 2)
select #current_month;
Result:
201802
If you really want to use case instead, you should change the 1 to 2:
set #current_month = CAST( year(getdate()) as varchar(4)) +
case when len(CAST(month(getdate()) as varchar(2))) < 2 then
'0' + CAST( month(getdate()) as varchar(2)) else
CAST( month(getdate()) as varchar(2)) end
declare #current_month varchar(6)
set #current_month = CAST(year(getdate())*100 + month(getdate()) as varchar(6))
Getting error:
Msg 512, Level 16, State 1, Line 48
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Msg 512, Level 16, State 1, Line 87
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Need to check each record in the table and update a table if there is no records = to 0,00. Then update an existing table with current values. I want to be able to track current data and compare with old data.
DECLARE #LocTime DATETIME;
DECLARE #MinDateTime DATETIME;
DECLARE #SystemDateTime DATETIME;
DECLARE #LIR MONEY;
DECLARE #LOAR MONEY;
SELECT #SystemDateTime = SYSDATETIME(); --= '2016-12-07 23:30:00'
SELECT #MinDateTime = DATEADD(mi,-30,#SystemDateTime) --go back half hour of reads, job is running every 10 minutes for overlap
--select #MinDateTime, #SystemDateTime
IF OBJECT_ID(N'tempdb..##LastOver2') IS NOT NULL
BEGIN
DROP TABLE ##LastOver2
END
--make temp table to hold found data
CREATE TABLE ##LastOver2 (ReadDate DATETIME
, FacilityName NVARCHAR(100)
, FacilityID UNIQUEIDENTIFIER
, LastInstantRead MONEY
, LastOverAllRead MONEY
, FacilityTimeZone INT
, FacilityTime DATETIME)
INSERT INTO ##LastOver2 (ReadDate, FacilityName
, FacilityID, LastInstantRead
, LastOverAllRead
, FacilityTimeZone, FacilityTime)
SELECT DISTINCT --why distinct?
fmr.ReadDate, f.Name
, f.FacilityID, fm.LastInstantRead
, fm.LastOverAllRead
, f.Timezone
, #LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK)
ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK)
ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime and ReadDate <= #SystemDateTime) --including on both side to continue with overlap
and (fm.IsVirtual = 0 and fm.ParentMeterID is NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0
select * from ##LastOver2
IF (select LastInstantRead from ##LastOver2) = 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) = 0.00
BEGIN
--UPDATE dbo.Facilities
--SET SuppressMonitoring = 1
-- FROM dbo.Facilities F
-- JOIN ##LastOver L
-- ON F.FacilityID = l.FacilityID
-- WHERE F.FacilityID = l.FacilityID
DECLARE #body_content NVARCHAR(150);
DECLARE #StartHour NVARCHAR(8) = '08:30:00' ;
DECLARE #StopHour NVARCHAR(8) = '16:00:00';
--why distinct, is it already distinct. Is is supposed to distinct the facility or meter?
DECLARE #SQLScript NVARCHAR(200) = 'SELECT distinct * FROM ##LastOver2 WHERE CONVERT(TIME, FacilityTime) BETWEEN '
+ CHAR(39) + #StartHour + CHAR(39) +' AND ' + CHAR(39) + #StopHour + CHAR(39);
--only looking for reads during day hours? shouldn't use between.
DECLARE #copyRecipients NVARCHAR(100)
SET #body_content = 'Please check the attached file. This was from server: ' + ##SERVERNAME
DECLARE #fileName nvarchar(255);
select #fileName = 'BadReading_' + replace(replace(convert(nvarchar(19),getdate(), 126),':',''),'-','') + '.txt';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SQLSupport'
,#recipients = 'Btest#test.com'
--, #recipients = 'jira#cleanenergycollective.atlassian.net'
--, #copy_recipients= #copyRecipients
--, #copy_recipients= 'Bill.Lugaila#easycleanenergy.com;yvonne.lewis#easycleanenergy.com;don.munroe#easycleanenergy.com;Justin.Reed#easycleanenergy.com'
, #query = #SQLScript
, #subject = 'Facility Meter Check and Updating table' --change so easier to see in emails
, #body= #body_content
, #importance= 'High'
, #attach_query_result_as_file = 1
, #query_attachment_filename = #fileName ;
--select #SQLScript
END
ELSE IF (select LastInstantRead from ##LastOver2) != 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) != 0.00
BEGIN
UPDATE [dbo].[_LastInstant_OverAll_Read]
SET [FacilityName] = lo.FacilityName,
[LastInstantRead] = lo.LastInstantRead,
[LastOverAllRead]= lo.LastOverAllRead,
[Time_Date] = (SELECT CONVERT(DateTime, SysDateTime()))
FROM ##LastOver2 lo
WHERE lo.FacilityName = [dbo].[_LastInstant_OverAll_Read].FacilityName
AND lo.LastInstantRead != [dbo].[_LastInstant_OverAll_Read].LastInstantRead
AND lo.LastOverAllRead != [dbo].[_LastInstant_OverAll_Read].LastOverAllRead
END
the following could be returning multiple rows:
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) = 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) = 0.00
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) != 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) != 0.00
if you are expecting them to return only 1 row then you will need to fix the issue with the following query:
SELECT DISTINCT --why distinct?
fmr.ReadDate,
f.Name,
f.FacilityID,
fm.LastInstantRead,
fm.LastOverAllRead,
f.Timezone,
#LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK) ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK) ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime
AND ReadDate <= #SystemDateTime) --including on both side to continue with overlap
AND (fm.IsVirtual = 0
AND fm.ParentMeterID IS NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0;
I have a table with 3 columns StartDate, EndDate, ElapsedTimeInSec.
I use an AFTER INSERT trigger to calculate the ElapsedTimeInSec.
I would like to do this:
If my start date is 2011-11-18 07:30:00 and my end date 2011-11-18 9:30:00 which give me a ElapsedtimeInSec of 7200 I would like to be able to split it this way.
Row 1 : 2011-11-18 07:30:00 / 2011-11-18 08:00:00 / 1800
Row 2 : 2011-11-18 08:00:00 / 2011-11-18 09:00:00 / 3600
Row 3 : 2011-11-18 09:00:00 / 2011-11-18 09:30:00 / 1800
How can I achieve this result ?
I dont think I made my explaination clear enough.
I have an actual table with data in it which as 2 field one with a StratDowntime and one with a EndDowntime and I would like to create a view of hours per hour base on a production shift of 12 hours (07:00:00 to 19:00:00) of the downtime.
So If I have a downtime from 2011-11-19 06:00:00 to 2011-11-19 08:00:00 I want in my report to see from 07:00:00 so the new rocrd should look like 2011-11-19 07:00:00 to 2011-11-19 08:00:00.
Another example if I do have downtime from 2011-11-19 10:30:00 to 2011-11-19 13:33:00 I should get in my report this
- 2011-11-19 10:30:00 to 2011-11-19 11:00:00
- 2011-11-19 11:00:00 to 2011-11-19 12:00:00
- 2011-11-19 12:00:00 to 2011-11-19 13:00:00
- 2011-11-19 13:00:00 to 2011-11-19 13:33:00
I hope this will clarify the question because none of the solution down there is actually doing this it is close but not on it.
thanks
You could try something like:
DECLARE #StartDate DATETIME = '11/18/2011 07:30:00',
#EndDate DATETIME = '11/18/2011 09:30:00',
#Runner DATETIME
IF DATEDIFF (mi, #StartDate, #EndDate) < 60
BEGIN
SELECT #StartDate,
#EndDate,
DATEDIFF (s, #StartDate, #EndDate)
RETURN
END
SET #Runner = CONVERT (VARCHAR (10), #StartDate, 101) + ' ' + CAST (DATEPART(hh, #StartDate) + 1 AS VARCHAR) + ':00:00'
WHILE #Runner <= #EndDate
BEGIN
SELECT #StartDate,
#Runner,
DATEDIFF (s, #StartDate, #Runner)
SET #StartDate = #Runner
SET #Runner = DATEADD(hh, 1, #Runner)
END
SET #Runner = CONVERT (VARCHAR (10), #EndDate, 101) + ' ' + CAST (DATEPART(hh, #EndDate) AS VARCHAR) + ':00:00'
SELECT #Runner,
#EndDate,
DATEDIFF (s, #Runner, #EndDate)
CTE:
DECLARE #beginDate DATETIME,
#endDate DATETIME
SELECT #beginDate = '2011-11-18 07:30:00',
#endDate = '2011-11-18 09:33:10'
DECLARE #mytable TABLE
(
StartDowntime DATETIME,
EndDowntime DATETIME,
ElapsedDowntimesec INT
)
-- Recursive CTE
;WITH Hours
(
BeginTime,
EndTime,
Seconds
)
AS
(
-- Base case
SELECT #beginDate,
DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate),
DATEDIFF
(
SECOND,
#beginDate,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate)
END
)
UNION ALL
-- Recursive
SELECT Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(minute, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END,
DATEDIFF
(
SECOND,
Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END
)
FROM Hours
WHERE Hours.BeginTime < #endDate
)
INSERT INTO #myTable
SELECT *
FROM Hours
WHERE BeginTime < #endDate
SELECT * FROM #myTable
Results
BeginTime EndTime Seconds
2011-11-18 07:30:00.000 2011-11-18 08:00:00.000 1800
2011-11-18 08:00:00.000 2011-11-18 09:00:00.000 3600
2011-11-18 09:00:00.000 2011-11-18 09:33:10.000 1990
You can use a table valued function applied like SELECT * FROM [dbo].split('2011-11-02 12:55:00','2011-11-02 13:05:00')
Function defintion:
CREATE FUNCTION [dbo].[split] (#d1 DATETIME, #d2 DATETIME)
RETURNS #result TABLE (
StartDate DATETIME,
EndDate DATETIME,
ElapsedTimeSeconds INT
)
AS
BEGIN
-- Store intermediate values in #tmp, using ix as driver for start times.
DECLARE #tmp TABLE (ix INT NOT NULL IDENTITY(0,1) PRIMARY KEY
, d1 DATETIME, d2 DATETIME)
-- Insert first hole hour lower than start time
INSERT INTO #tmp (d1) SELECT DATEADD(HOUR, DATEDIFF(HOUR, -1, #d1), -1)
-- Calculate expected number of intervals
DECLARE #intervals INT = DATEDIFF(HOUR, #d1, #d2) - 1
-- insert all intervals
WHILE #intervals > 0
BEGIN
INSERT INTO #tmp (d1, d2) select top 1 d1, d2 FROM #tmp
SET #intervals = #intervals - 1
END
-- Set start and end time for all whole hour intervals
UPDATE #tmp SET d1 = DATEADD(hour, ix, d1)
, d2 = DATEADD(hour, ix + 1, d1)
-- Set correct start time for first interval
UPDATE #tmp SET d1 = #d1 WHERE d1 <= #d1
-- Insert end interval
INSERT INTO #tmp (d1, d2)
SELECT MAX(d2), #d2 FROM #tmp
-- Delete non-empty last interval
DELETE FROM #tmp WHERE d1 = d2
-- Insert #tmp to #result
INSERT INTO #result (StartDate, EndDate)
SELECT d1, d2 FROM #tmp
-- Set interval lengths
UPDATE #result SET ElapsedTimeSeconds = DATEDIFF(second, StartDate, EndDate)
return
END
GO
To get a result from an existing table, you can use CROSS APPLY. Assuming a table YourTable with StartTime and EndTime you can do something like
SELECT s.*, y.* FROM YourTable y
cross apply dbo.split(y.StartTime, y.EndTime) s
WHERE y.EndTime < '2011-09-11'
to get a result with a kind of join between input data and output table.
I can't find a simple way to do this in T-SQL.
I have for example a column (SortExport_CSV) that returns an integer '2' thru 90.
If the stored number is a single digit, I need it to convert to a 2 digit string that begins with a 0.
I have tried to use CAST but I get stuck on how to display the style in the preferred format (0#)
Of course it is easy to do this on the front end (SSRS, MSAccess, Excel, etc) but in this instance I have no front end and must supply the raw dataset with the already formatted 2 digit string.
select right ('00'+ltrim(str( <number> )),2 )
You can use T-SQL's built in format function:
declare #number int = 1
select format (#number, '0#')
SELECT RIGHT('0' + CAST(sortexport_csv AS VARCHAR), 2)
FROM your_table
You're all doing too much work:
right(str(100+#x),2)
-- for a function, same idea:
--
create function zeroPad( #yourNum int, #wid int)
as
begin
return right( 1000000+#yourNum), #wid)
end
Convert the value to a string, add a zero in front of it (so that it's two or tree characters), and get the last to characters:
right('0'+convert(varchar(2),Sort_Export_CSV),2)
DECLARE #Number int = 1;
SELECT RIGHT('0'+ CONVERT(VARCHAR, #Number), 2)
--OR
SELECT RIGHT(CONVERT(VARCHAR, 100 + #Number), 2)
GO
Here is tiny function that left pad value with a given padding char
You can specify how many characters to be padded to left..
Create function fsPadLeft(#var varchar(200),#padChar char(1)='0',#len int)
returns varchar(300)
as
Begin
return replicate(#PadChar,#len-Len(#var))+#var
end
To call :
declare #value int; set #value =2
select dbo.fsPadLeft(#value,'0',2)
You could try this
SELECT RIGHT( '0' + convert( varchar(2) , '0' ), 2 ) -- OUTPUTS : 00
SELECT RIGHT( '0' + convert( varchar(2) , '8' ), 2 ) -- OUTPUTS : 08
SELECT RIGHT( '0' + convert( varchar(2) , '9' ), 2 ) -- OUTPUTS : 09
SELECT RIGHT( '0' + convert( varchar(2) , '10' ), 2 ) -- OUTPUTS : 10
SELECT RIGHT( '0' + convert( varchar(2) , '11' ), 2 ) -- OUTPUTS : 11
this should help
here you go
select RIGHT(REPLICATE('0', 2) + CAST(2 AS VARCHAR(2)), 2)
should return 02
try
right('0' + convert(varchar(2), #number),2)
Another example:
select
case when teamId < 10 then '0' + cast(teamId as char(1))
else cast(teamId as char(2)) end
as 'pretty id',
* from team
Try this
--Generate number from 2 to 90
;with numcte as(
select 2 as rn
union all
select rn+1 from numcte where rn<90)
--Program that formats the number based on length
select case when LEN(rn) = 1 then '00'+CAST(rn as varchar(10)) else CAST(rn as varchar(10)) end number
from numcte
Partial Output:
number
002
003
004
005
006
007
008
009
10
11
12
13
14
15
16
17
18
19
20
SELECT
replace(str(month(DATEADD(month, -1, '2012-02-29')), 2),' ' , '0')
You can create a function like this:
CREATE FUNCTION [dbo].[f_convert_int_to_2_digits](#input int)
RETURNS varchar(10)
AS
BEGIN
--return value
DECLARE #return varchar(10)
if #input < 10
begin
set #return = '0' + convert(varchar(1), #valor)
end
else
begin
set #return = convert(varchar(10), #input)
end
-- return result
RETURN #return
END
and then use it everywhere:
select [dbo].[f_convert_int_to_2_digits](<some int>)
Example for converting one digit number to two digit by adding 0 :
DECLARE #int INT = 9
SELECT CASE WHEN #int < 10
THEN FORMAT(CAST(#int AS INT),'0#')
ELSE
FORMAT(CAST(#int AS INT),'0')
END
DECLARE #Number int = 1;
SELECT RIGHT('0'+ CONVERT(VARCHAR, #Number), 2)
--OR
SELECT RIGHT(CONVERT(VARCHAR, 100 + #Number), 2)
GO
The simplest way is:
declare
#len int = 10,
#number int = 122222,
#prefix char = '0'
select replicate(#prefix, #len - len(#number)) + CAST(#number AS VARCHAR)
result:
0000122222