I am wanting to use a case statement to change the time piece only of a datetime field in SQL Server 2008 I have a query that will change the time piece, but I need to know how to keep the date portion intact so only the time piece is altered. Meaning if the datetime is
01/01/2015 08:45:10.863
with my syntax I would want to alter it to
01/01/2015 08:30:00.000
This is my syntax which as I said will change the time portion but it does not retain the date. How can I keep the date and change the time portion only?
Create Table #Test
(
[charactername] varchar(100)
,[lefttabletime] datetime
)
Insert Into #Test Values
('Bob Goblin', '01/01/2015 08:14:23.000'),
('Grab Crab', '01/01/2015 08:30:56.023'),
('Mike Knight', '01/01/2015 08:45:10.863')
Select
[charactername]
,case when CAST([lefttabletime] As TIME) > '08:40:00.000' THEN '08:30:00.000'
else [lefttabletime]
FROM #Test
Drop Table #Test
EDIT Additional Syntax Attempted
This threw an error of:
Msg 241, Level 16, State 1, Line 10
Conversion failed when converting date and/or time from character string.
And I tried this syntax
Select
[charactername]
,case when CAST([lefttabletime] As TIME) > '08:40:00.000'
THEN CAST(CAST(CONVERT(DATE, [lefttabletime],101) AS VARCHAR)
+ '08:40:00.000' AS DATETIME) else [lefttabletime] end
FROM #Test
I find it easier if you split the leftabletime into a date and a time component first, the recombine them with the new time portion:
;WITH cte AS
(
SELECT charactername,
[date] = CAST(lefttabletime as date),
[time] = CAST(lefttabletime as time)
FROM #test
)
SELECT charactername,
CAST([date] as datetime)
+ CAST(CASE WHEN [time] > '08:40:00' THEN '08:30:00' ELSE [time] END as datetime)
FROM cte
You can merge the two statement together but I like the simplicity and clarity that a separate CTE provides.
I usually try to make datetime rounding problems fit the DATEADD/DATEDIFF pattern, and I've managed to do that here:
Create Table #Test
(
[charactername] varchar(100)
,[lefttabletime] datetime
)
Insert Into #Test Values
('Bob Goblin', '2015-01-01T08:14:23.000'),
('Grab Crab', '2015-01-01T08:30:56.023'),
('Mike Knight', '2015-01-01T08:45:10.863')
select charactername,
CASE WHEN DATEDIFF(minute,'20010101',lefttabletime)%60 >= 40 THEN
DATEADD(minute,((DATEDIFF(minute,'20010101',lefttabletime)/30)*30),'20010101')
ELSE lefttabletime END
from #Test
Drop Table #Test
The expression DATEADD(minute,((DATEDIFF(minute,'20010101',lefttabletime)/30)*30),'20010101') rounds the time of a datetime down to the nearest 30 minutes (20010101 is an arbitrary date and doesn't need to be adjusted in any way).
I also just use a separate DATEDIFF to find the required matching condition. Where possible, with datetime data, I try to keep it in datetime variables, or, at worst, ints. As soon as you convert to strings you have to start worrying about formats, etc, which I'd usually rather avoid.
Result:
charactername
--------------------- -----------------------
Bob Goblin 2015-01-01 08:14:23.000
Grab Crab 2015-01-01 08:30:56.023
Mike Knight 2015-01-01 08:30:00.000
Related
I am trying to analyze order_Date column and column have multiple date format i want to convert all those date in same format which wull make be easier to analyze the order_date.
I am trying to analyze the order_date however this column have multiple date format 2019/07/15 and 1/13/2014
Howeever, while converting different format date with one format yyyy/mm/dd with query.
select date_format(order_date, '%y/%m/%d'),orderid from superstore;
it shows null values like this.
i have tried to use `CAST as well but it shows every single value as null.
select case when order_date like '%Y' then date_format(order_date, '%Y/%m/%d') else null end as newdate from superstore;
date_format funtion is used to format a date datatype you should use https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_str-to-date any null values returned by str_to_date either failed or started as null. You will need to examine these and adjust the str_to_date parameters appropriately. There is a catch though is 20/2/20 y/m/d or d/m/y (for example) and how can you differentiate month and day where both are <=12?
For example
drop table if exists t;
create table t
(dt varchar(10));
insert into t values
('1/1/2020'),('2020/1/12'),('12/12/12'),(null),('13-14-15');
select dt,
case when length(substring_index(dt,'/',-1)) = 4 then str_to_date(dt,'%d/%m/%Y')
when length(substring_index(dt,'/',1)) = 4 then str_to_date(dt,'%Y/%m/%d')
when length(substring_index(dt,'/',1)) = 2 then str_to_date(dt,'%y/%m/%d')
else str_to_date(dt,'%y/%m/%d')
end dateformatted
from t;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=553219f33ad9e9a4404fc4c0cb6571c9
note in no case can I identify month and day and sometimes year..
I have been working on copying data from one table to another (Archival) & being doing that manually today. I have ben trying to select data from one table to another table based on datetime. Data for 01st Jan is inserted/committed, similarly data for 02nd Jan is inserted/committed & this runs in iterations until the max date is reached based on the source table max date.
I have a table named test_data with a datetime column named check_time & a similar table is created named test_arc.
I also tried to declare a variable that gets the MIN(check_time) from test_data & starts iterations from that date & goes all the way till the MAX(check_time) but that also seems to fail to set values. Please see what I attempted in the link below.
This seems quite feasible in MS SQL & works like a charm but I can't get the same logic to work on MySQL. I have to agree I am not well versed with MySQL.
I tested using DB Fiddle but keep running into issues. Can someone point me in the right direction. Thanks
DB Fiddle Link: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=f20dfefe1477993c870dde178825380f
MS SQL:
DECLARE #MinDate DATETime = '2022-01-01 00:00:00',
#MaxDate DATETime = getdate();
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
CONVERT(varchar,DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY
a.object_id) - 1, #MinDate),120) as
startdate,CONVERT(varchar,DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY
a.object_id) - 1, #MinDate) +'23:59:59.999',120) as Enddate
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
Look at this code:
CREATE PROCEDURE `insert_data` ( IN date_from DATE,
IN date_till DATE )
BEGIN
-- adjust input dates according to the values present in the table
SELECT GREATEST(DATE(MIN(check_time)), date_from),
LEAST(DATE(MAX(check_time)), date_till)
INTO date_from, date_till
FROM test_data;
-- copy the data day-by-day
WHILE date_from <= date_till DO
INSERT IGNORE INTO test_arc
SELECT *
FROM test_data
WHERE check_time >= date_from AND check_time < date_from + INTERVAL 1 DAY;
SET date_from = date_from + INTERVAL 1 DAY;
END WHILE;
END
fiddle with debug output.
You may add a transaction into the cycle body if needed.
I am using MySQL Version 5.7.28. I am having json data like below.
CREATE TABLE `week2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` smallint(1),
`json` text ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO week2(id,type,json)
VALUES
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(122,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
As you see, the json column has nested JSON data. so here i am looking to compare today's day ( which is Saturday and we are currently in between start time and end time)
Expected Result:
(121,1,'[{"weekdays":"Sunday"},{"weekdays":"Monday"},{"weekdays":"Tuesday"},{"weekdays":"Wednesday"},{"weekdays":"Thursday"},{"weekdays":"Friday"},{"weekdays":"Saturday"}]'),
(123,2,'[{"start_time":"08:00 AM","end_time":"10:00 PM"}]');
You need to detect day name (1) and time period (2), and combine those two conditions by OR operator at the end.
For (1) : Detect the current day's name by using DAYNAME() function and search whether exists in the JSON data containing weekday keys through use of JSON_CONTAINS function.
For (2) : TIME() function and CASTing strings to TIME data type might be used with a trick to add 12 hours iterations for the cases of PM type times.
So, consider using :
SELECT *
FROM `week2`
WHERE JSON_CONTAINS(`json`->>'$[*].weekdays', CONCAT('"',DAYNAME( NOW() ),'"')) = 1
OR
(
TIME(ADDTIME(NOW(),"8:00:00")) >=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST((REPLACE(`json`->>'$[0].start_time',"12:00 AM","00:00 AM")) AS TIME)
END
AND
TIME(ADDTIME(NOW(),"8:00:00")) <=
CASE WHEN INSTR(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"),"PM")>0
THEN
CAST(CONCAT(MOD((TIME_FORMAT(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM"), "%T")+12),24),":00 AM")
AS TIME)
ELSE
CAST(REPLACE(`json`->>'$[0].end_time',"12:00 AM","00:00 AM") AS TIME)
END)
Demo
Perhaps, something like this:
SELECT *,CURRENT_TIME
FROM
(SELECT *,JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].start_time')) AS st,
JSON_UNQUOTE(JSON_EXTRACT(`json`,'$[0].end_time')) AS et FROM week2) V
WHERE (JSON_SEARCH(`json`, 'one', DAYNAME(CURDATE())) IS NOT NULL
OR
CURRENT_TIME hour >=
CASE WHEN st LIKE '%AM%' THEN REPLACE(st,' AM',':00')
WHEN st LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(st,' PM',':00'))+43200)
END
AND
CURRENT_TIME hour <=
CASE WHEN et LIKE '%AM%' THEN REPLACE(et,' AM',':00')
WHEN et LIKE '%PM%' THEN SEC_TO_TIME(TIME_TO_SEC(REPLACE(et,' PM',':00'))+43200)
END);
First query is using JSON_EXTRACT to extract the time and JSON_UNQUOTE to remove the (") then make it as a sub-query.
On the outer-query, find the weekdays value using JSON_SEARCH compare with today's DAYNAME(CURDATE()); if JSON_SEARCH doesn't find it, it will return NULL, hence the IS NOT NULL condition is used.
Append OR to compare CURRENT_TIME with the time value extracted from the json field. But first, using CASE expression to find out if it's AM or PM; if it's AM, just REPLACE the AM with seconds hand (:00). If it's PM, do the REPLACE like AM then convert the time value to seconds using TIME_TO_SEC then add 43200 seconds (12 hours) and convert again to standard hour:minute:second format using SEC_TO_TIME.
P/S: It was my attempt to answer but I didn't post it because I get wrong result with the time.. Only when I put CURRENT_TIME in the fiddle that I realized that the fiddle time zone is different that mine. Also, I was testing on my local DB; which is MariaDB and the results of converting string to time using STR_TO_TIME is different. Honestly, in my opinion, if this is MariaDB, I think there's a chance that the query is much shorter.
table:
--duedate timestamp
--submissiondate timestamp
--blocksreq numeric
--file clob
--email varchar2(60)
Each entry is a file which will take blocksreq to accomplish. There are 8 blocks allotted per day (but could be modified later). before i insert into the table, i want to make sure there are enough blocks to accomplish it in the timeframe of NOW() and #duedate
I was thinking of the following, but i think i am doing it wrong:
R1 = select DAY(), #blocksperday - sum(blocksreq) as free
from table
where #duedate between NOW() and #duedate
group by DAY()
order by DAY() desc
R2 = select sum(a.free) from R1 as a;
if(R2[0] <= #blocksreq){ insert into table; }
pardon the partial pseudocode.
SQL FIDDLE: http://sqlfiddle.com/#!2/5bda5
warning: My sql fiddle has garbage code... as i dont know how to make a lot of test cases. nor set the duedate to NOW()+5 days
Something like this? (wasn't sure how partial days were handled so ignored that part)
CREATE TABLE `DatTable` (
`duedate` datetime DEFAULT NULL,
`submissiondate` datetime DEFAULT NULL,
`blocksreq` smallint(6) DEFAULT NULL
)
SET #duedate:='2012-10-15';
SET #submissiondate:=CURRENT_TIMESTAMP;
SET #blocksreq:=5;
INSERT INTO DatTable(duedate,submissiondate,blocksreq)
SELECT #duedate,#submissiondate,#blocksreq
FROM DatTable AS b
WHERE duedate > #submissiondate
HAVING COALESCE(SUM(blocksreq),0) <= DATEDIFF(#duedate,#submissiondate)*8-#blocksreq;
Lets say I have a table that contains the following - id and date (just to keep things simple).
It contains numerous rows.
What would my select query look like to get the average TIME for those rows?
Thanks,
Disclaimer: There may be a much better way to do this.
Notes:
You can't use the AVG() function against a DATETIME/TIME
I am casting DATETIME to DECIMAL( 18, 6 ) which appears to yield a reasonably (+- few milliseconds) precise result.
#1 - Average Date
SELECT
CAST( AVG( CAST( TimeOfInterest AS DECIMAL( 18, 6 ) ) ) AS DATETIME )
FROM dbo.MyTable;
#2 - Average Time - Remove Date Portion, Cast, and then Average
SELECT
CAST( AVG( CAST( TimeOfInterest - CAST( TimeOfInterest AS DATE ) AS DECIMAL( 18, 6 ) ) ) AS DATETIME )
FROM dbo.MyTable;
The second example subtracts the date portion of the DATETIME from itself, leaving only the time portion, which is then cast to a decimal for averaging, and back to a DATETIME for formatting. You would need to strip out the date portion (it's meaningless) and the time portion should represent the average time in the set.
SELECT CAST(AVG(CAST(ReadingDate AS real) - FLOOR(CAST(ReadingDate as real))) AS datetime)
FROM Rbh
I know that, in at least some of the SQL standards, the value expression (the argument to the AVG() function) isn't allowed to be a datetime value or a string value. I haven't read all the SQL standards, but I'd be surprised if that restriction had loosened over the years.
In part, that's because "average" (or arithmetic mean) of 'n' values is defined to be the sum of the values divided by the 'n'. And the expression '01-Jan-2012 08:00' + '03-Mar-2012 07:53' doesn't make any sense. Neither does '01-Jan-2012 08:00' / 3.
Microsoft products have a history of playing fast and loose with SQL by exposing the internal representation of their date and time data types. Dennis Ritchie would have called this "an unwarranted chumminess with the implementation."
In earlier versions of Microsoft Access (and maybe in current versions, too), you could multiply the date '01-Jan-2012' by the date '03-Mar-2012' and get an actual return value, presumably in units of square dates.
If your dbms supports the "interval" data type, then taking the average is straightforward, and does what you'd expect. (SQL Server doesn't support interval data types.)
create table test (
n interval hour to minute
);
insert into test values
('1:00'),
('1:30'),
('2:00');
select avg(n)
from test;
avg (interval)
--
01:30:00