Do any database engines have the concept of a C-like macro? Here would be an example where I'd just like to make it more readable:
SELECT
SUM(Profit) AS Total,
(SELECT AVG(Profit) FROM This
WHERE Category=This.Category AND Product=This.Product
AND PARSE_DATE('%M %d%, Y', CONCAT(This.Month ' 1, ' This.Year))
BETWEEN PARSE_DATE('%M %d%, Y', CONCAT(This.Month ' 1, ' This.Year))
AND PARSE_DATE('%M %d%, Y', CONCAT(This.Month ' 1, ' This.Year))-INTERVAL 3 MONTH
FROM Tbl
I would rather have something that looks like:
#define dt PARSE_DATE('%M %d%, Y', CONCAT(This.Month ' 1, ' This.Year))
SELECT
SUM(Profit) AS Total,
(SELECT AVG(Profit) FROM This
WHERE Category=This.Category AND Product=This.Product
AND dt BETWEEN dt AND (dt-INTERVAL 3 MONTH)
FROM Tbl
Does something like that exist or commonly-used in the major DBMSs?
From Oracle 12, you can declare a function inside a sub-query factoring (WITH) clause:
WITH FUNCTION dt (month INT, year INT) RETURN DATE AS
BEGIN
RETURN TO_DATE(year || '-' || month || '-01', 'YYYY-MM-DD');
END;
SELECT *
FROM this
WHERE dt(this.month, this.year)
BETWEEN ADD_MONTHS(dt(this.month, this.year), -3)
AND dt(this.month, this.year);
db<>fiddle here
From Oracle 21, you can write SQL macros:
CREATE FUNCTION dt (month INT, year INT)
RETURN VARCHAR2 SQL_MACRO(SCALAR)
AS
BEGIN
RETURN 'TO_DATE(year || ''-'' || month || ''-01'', ''YYYY-MM-DD'')';
END;
/
Then would use it as:
SELECT *
FROM this
WHERE dt(this.month, this.year)
BETWEEN ADD_MONTHS(dt(this.month, this.year), -3)
AND dt(this.month, this.year);
And the query would get rewritten as:
SELECT *
FROM this
WHERE TO_DATE(this.year || '-' || this.month || '-01', 'YYYY-MM-DD')
BETWEEN ADD_MONTHS(TO_DATE(this.year || '-' || this.month || '-01', 'YYYY-MM-DD'), -3)
AND TO_DATE(this.year || '-' || this.month || '-01', 'YYYY-MM-DD');
I have the following MySQL code that according to me should return 0:
mod( mod((180 / (30.4166666667 * 24 * 60)),1) * 30.4166666667,1) * 24 as HoursWorked
=> return 3.000
mod(mod(mod((180 / (30.4166666667 * 24 * 60)),1) * 30.4166666667,1) * 24,1) as ModHoursWorked
=> return 1
What am I missing?
the division must be floored
select mod(mod(mod(( floor(180 / (30.4166666667 * 24 * 60)) ),1) * 30.4166666667,1) * 24,1) ;
This worked:
SELECT truncate(mod(mod(mod((180 / (30.4166666667 * 24 * 60)),1) * 30.4166666667, 1) * 24, 1), 0) AS ModHoursWorked; => return 0
I have a database column containing an integer value that represents a systems up time in seconds. I'd really like a query to be able to show me that up time in a easy to read format day(s) hour(s) minute(s) but I'm not quite sure how to do it. A lot of examples I've found appear to use parameters as an example but never much of how to use it in a select function.
I need the time to be the same as what's displayed on a website too. I tried one query earlier and its added days and removed minutes. Can anyone help me out?
Source data:
PDT0014 6141
PDT0008 4990
PDT0024 840227
PDT0033 2301
PDT0035 5439
PDT0005 3434
PDT0019 5482
Sample code:
SELECT tblAssets.AssetName,
(case when tblAssets.Uptime> (24*60*60)
then
cast(datepart(day,datediff(dd, 0, dateadd(second, tblAssets.Uptime, 0))) as varchar(4))
+ ' Day(s) ' + convert(varchar(2), dateadd(second, tblAssets.Uptime, 0), 108) +' Hour(s)'
else
convert(varchar(5), dateadd(second, tblAssets.Uptime, 0), 108) + ' Hour(s) Minute(s) '
end) AS Uptime
FROM tblAssets
Desired Query Output:
PDT0014 01:42 Hour(s) Minute(s)
PDT0008 01:23 Hour(s) Minute(s)
PDT0024 10 Day(s) 17 Hour(s)
PDT0033 00:38 Hour(s) Minute(s)
PDT0035 01:30 Hour(s) Minute(s)
PDT0005 00:57 Hour(s) Minute(s)
PDT0019 01:31 Hour(s) Minute(s)
Depending on the output you want:
DECLARE #s INT = 139905;
SELECT CONVERT(VARCHAR(12), #s /60/60/24) + ' Day(s), '
+ CONVERT(VARCHAR(12), #s /60/60 % 24)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), #s /60 % 60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), #s % 60), 2);
Result:
1 Day(s), 14:51:45
Or:
DECLARE #s INT = 139905;
SELECT
CONVERT(VARCHAR(12), #s /60/60/24) + ' Day(s), '
+ CONVERT(VARCHAR(12), #s /60/60 % 24) + ' Hour(s), '
+ CONVERT(VARCHAR(2), #s /60 % 60) + ' Minute(s), '
+ CONVERT(VARCHAR(2), #s % 60) + ' Second(s).';
Result:
1 Day(s), 14 Hour(s), 51 Minute(s), 45 Second(s).
You can replace 60/60/24 with 86400 etc. but I find it better self-documenting if you leave in the /seconds/minutes/hours calculations. And if you are going against a table, just use column_name in place of #s.
I tend to use:
CAST(FLOOR(seconds / 86400) AS VARCHAR(10))+'d ' +
CONVERT(VARCHAR(5), DATEADD(SECOND, Seconds, '19000101'), 8)
The top part just gets your days as an integer, the bottom uses SQL-Server's convert to convert a date into a varchar in the format HH:mm:ss after converting seconds into a date.
e.g.
SELECT Formatted = CAST(FLOOR(seconds / 86400) AS VARCHAR(10))+'d ' +
CONVERT(VARCHAR(5), DATEADD(SECOND, Seconds, '19000101'), 8),
Seconds
FROM ( SELECT TOP 10
Seconds = (ROW_NUMBER() OVER (ORDER BY Object_ID) * 40000)
FROM sys.all_Objects
ORDER BY Object_ID
) S
Example on SQL Fiddle
N.B. Change CONVERT(VARCHAR(5), DATEADD(.. to CONVERT(VARCHAR(8), DATEADD(.. to keep the seconds in the result
EDIT
If you don't want seconds and need to round to the nearest minute rather than truncate you can use:
SELECT Formatted = CAST(FLOOR(ROUND(Seconds / 60.0, 0) * 60 / 86400) AS VARCHAR(10))+'d ' +
CONVERT(VARCHAR(5), DATEADD(SECOND, ROUND(Seconds / 60.0, 0) * 60, '19000101'), 8),
Seconds
FROM ( SELECT Seconds = 3899
) S
I have just replaced each reference to the column seconds with:
ROUND(Seconds / 60.0, 0) * 60
So before doing the conversion rounding your seconds value to the nearest minute
You can convert seconds to days by dividing by 86400
You can convert seconds to hours by dividing by 3600, but you need to get the remainder
(by subtracting off the total days converted to hours)
You can convert seconds to minutes by dividing by 60, but you need to get the remainder (by subtracting off the total hours converted to minutes)
Seconds you can just report, but like minutes you want to only report the remainder of seconds (by sutracting off the total minutes converted to seconds)
SELECT FLOOR( UpTime / 86400 ) AS DAYS
, FLOOR( ( UpTime / 3600 ) - FLOOR( UpTime / 86400 ) * 24 ) AS HOURS
, FLOOR( ( UpTime / 60 ) - FLOOR( UpTime / 3600 ) * 60 ) AS MINUTES
, UpTime - FLOOR( UpTime / 60 ) * 60 AS SECONDS
FROM ( SELECT 269272 AS UpTime ) AS X
269272 represents 3 days (259200 seconds), 2 hours (7200 seconds), 47 minutes (2820 seconds) and 52 seconds.
This query produces:
| DAYS | HOURS | MINUTES | SECONDS |
------------------------------------
| 3 | 2 | 47 | 52 |
Substituting 125 (2 minutes, 5 seconds) for 259200 will produce:
| DAYS | HOURS | MINUTES | SECONDS |
------------------------------------
| 0 | 0 | 2 | 5 |
To convert this to a string representation, you can use SQL Server 2012's FORMAT function:
SELECT CASE
WHEN DAYS > 0 THEN
FORMAT( DAYS, '##' ) + ' Day(s) ' + FORMAT( HOURS, '##' ) + ' Hour(s)'
ELSE
FORMAT( HOURS, '##' ) + ':' + FORMAT( MINUTES, '##' ) + ' Hour(s) Minute(s)'
END AS UpTimeString
FROM (
SELECT FLOOR( UpTime / 86400 ) AS DAYS
, FLOOR( ( UpTime / 3600 ) - FLOOR( UpTime / 86400 ) * 24 ) AS HOURS
, FLOOR( ( UpTime / 60 ) - FLOOR( UpTime / 3600 ) * 60 ) AS MINUTES
, UpTime - FLOOR( UpTime / 60 ) * 60 AS SECONDS
FROM ( SELECT 125 AS UpTime ) AS X
) AS UptimeSubselect
This is another approach using DATEPART():
DECLARE #S INT = 86472,
#START DATETIME = CONVERT(DATETIME,0)
DECLARE #END DATETIME = DATEADD(SECOND,#S, #START)
SELECT CONVERT(VARCHAR(10),DATEPART(DAY,#END)-1) + ' Day(s) ' +
RIGHT(CONVERT(VARCHAR(10),100+DATEPART(HOUR, #END)),2) + ':' +
RIGHT(CONVERT(VARCHAR(10),100+DATEPART(MINUTE, #END)),2) + ':' +
RIGHT(CONVERT(VARCHAR(10),100+DATEPART(SECOND, #END)),2)
If you don't need to format time part:
SELECT CONVERT(VARCHAR(10),DATEPART(DAY,#END)-1) + ' Day(s) ' +
CONVERT(VARCHAR(10),DATEPART(HOUR, #END)) + ' Hour(s)' +
CONVERT(VARCHAR(10),DATEPART(MINUTE, #END)) + ' Minute(s)' +
CONVERT(VARCHAR(10),DATEPART(SECOND, #END)) + ' Second(s)'
DECLARE #Seconds INT = 86200;
SELECT CONVERT(VARCHAR(15),
CAST(CONVERT(VARCHAR(12), #Seconds / 60 / 60 % 24)
+':'+ CONVERT(VARCHAR(2), #Seconds / 60 % 60)
+':'+ CONVERT(VARCHAR(2), #Seconds % 60) AS TIME), 100) AS [HH:MM:SS (AM/PM)]
I am working with some legacy tables that represent time as a decimal representating time like this:
74447.548 = 7:44:47.548
I am moving this to a table where time is stored as (int) milliseconds. I want to create a function for this conversion.. The following works, but is there a more efficient way??
CREATE FUNCTION `test`.`decimalToMilli` (bigTime decimal)
RETURNS INTEGER
BEGIN
return (floor(mod(bigTime,floor(bigTime))*1000) -- milliseconds
+ (floor(bigTime) MOD 100) * 1000 -- seconds
+ ((((floor(bigTime) - floor(bigTime) MOD 100) MOD 10000))/100) * 1000*60 -- minutes
+ ((((floor(bigTime) - floor(bigTime) MOD 10000) MOD 1000000))/10000) * 1000*60*60 --hrs
);
END
Suggestions for a better way?
What about this?
CREATE FUNCTION `test`.`decimalToMilli` (bigTime decimal)
RETURNS INTEGER
BEGIN
return ((bigTime * 1000) MOD 100000 -- seconds and milliseconds
+ (floor(bigTime / 100) MOD 100) * 60000 --minutes
+ (floor(bigTime / 10000)) * 3600000 -- hours
);
END
How can I pass from seconds to time on mysql?
the date format of out is hh:mm:ss.ms
Sorry. I need for example 0.98 sec -> 00:00:00.980; With sec_to_tiem return 00:00:01.
thanks.
I have implemeted of this way:
select concat(if(floor(seconds/(60 * 60)) = 0,'00',lpad(floor(seconds/(60 * 60)),2,'0')),
':',
if(floor(seconds/60) = 0,'00',lpad(floor(seconds / 60),2,'0')),
':',
seconds % 60)
but it have to exist other way more efficient
other way:
CONCAT(lpad(floor(seconds / 3600), 2, '0'), ':',
lpad(floor(seconds / 60), 2, '0'), ':',
lpad(floor(seconds % 60), 2, '0'), '.',
lpad(SUBSTRING_INDEX((seconds * 1000) % 1000, '.', 1), 3, '0'))
SELECT MAKETIME (<yourseconds>/(60*24), <yourseconds>/60, <yourseconds>%60)
or with format
SELECT TIME_FORMAT( MAKETIME( <yourseconds>/ ( 60 *24 ) , <yourseconds>/60, <yourseconds>%60 ) , '%T.%f' )