Splitting a Time into Twelve Equal Parts - sql-server-2008

I am trying to get value of 12 equal parts of the night length.
This is what my table looks like:
sunrise_time sunset_time Day_Length Night_length
2014-01-01 06:02:41.000 2014-01-01 20:44:05.000 14:41:24.0000000 09:18:36.0000000
This is my query, but getting day_length instead of night_light:
select (convert(varchar(10),dateadd(ss,abs(datediff(ss,sunrise_time,sunset_time))/12,0),8)) as nighthour
from table1
Expected output: 00:46:33
Actual output: 01:13:27
What's wrong with my query?

Switch your start date and end date around in the datediff to avoid issues. Here's a small change to your code.
select convert(varchar(10), dateadd(ss, datediff(ss, 0, night_length) / 12, 0), 8)
from table1

Related

MySQL STR_TO_DATE Problem while using this function

As title, I'm trying to convert a VARCHAR column in a DATE column, and data is populated in that format "DDMMYYYY" ex. XMAS is "25122022" and in this case the correct formula should be STR_TO_DATE(column, '%d%m%Y')Well, when I execute this query I get an error since in some cases I have values with a "missing" char, I mean, for example, "1012023" when the day is <10 the query fails, cause it checks for "01122023" instead.I could solve this easily by adding a 0 to all fields having length 7, but I'd like to make it more clean.Reading better the usage of STR_TO_DATE I noticed that I could replace %d with %e since the second choice should theorically consider days from 0 to 31 instead of 01 to 31.Unexpectedly the query didn't work and gave me the same erorr at the first instance of a length 7 string.Am I doing something wrong?Thanks in advance.
We can try left padding your date string with zero to a length of 8:
WITH yourTable AS (
SELECT '1012023' AS dt
)
SELECT STR_TO_DATE(LPAD(dt, 8, '0'), '%d%m%Y') AS dt_out -- 2023-01-01
FROM yourTable;
Demo

Cast works on one field but not other

I have two fields in my table, Cycle and Idle, both are nvarchar(50) and both contain time. I am trying to get a total for each field based on a particular program number using TSQL. Below is a small example of the data.
Id ProgramNo Cycle Idle
209702 3998_BOTH_OPPS.MPF 00:02:41 00:00:25
209703 472_7580_OPP1.MPF 00:02:08 00:01:44
209704 3998_BOTH_OPPS.MPF 00:00:27 00:00:11
209705 3998_BOTH_OPPS.MPF 00:00:00 00:00:00
209706 3998_BOTH_OPPS.MPF 00:00:01 00:01:40
209707 9491_OPP1.MPF 00:00:00 00:00:00
209708 9491_OPP1.MPF 00:00:01 00:00:04
209709 9491_OPP1.MPF 00:01:05 00:00:19
So for example, get the total Cycle time and Idle time for ProgramNo 3998_BOTH_OPPS.MPF and 9491_OPP1.MPF
This is my query...
SELECT
ProgramNo,
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(Cycle AS DATETIME))), 0) AS TIME) AS CycleTime,
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(Idle AS DATETIME))), 0) AS TIME) AS IdleTime
FROM Cycle
GROUP BY ProgramNo
It works just fine for CycleTime but I get an error for IdleTime:
"Conversion failed when converting date and/or time from character string."
Any suggestions? Thank you in advance.
You need to find values that don't match the HH:MM:SS format.
Here is one simplistic method:
select idle_time
from cycle
where idle_time not like '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]'
If that doesn't work, then look at the components:
where (idle_time like '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' and
(not (left(idle_time, 2) between '00' and '23') or
not substring(idle_time, 4, 2) between '00' and '59') or
not right(idle_time, 2) between '00' and '59')
)
SQL Server 2012+ makes this much easier with try_convert().
Based on your comment above, you can fix your data like so:
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(right(Idle,8) AS DATETIME))), 0) AS TIME) as IdleTime
We are just looking at the right most 8 characters... so if there isn't a #. before your time it will still work
select right('3.18:15:05',8)
select right('18:15:05',8)

Convert integer into datetime (dd:hh:mm:ss)

SELECT AVG (closeTime - createTime)
FROM Deals
WHERE dealid = 123
The 'closeTime' and 'addTime' have a DATETIME ( YYYY-MM-DD HH:MI:SS ) format. The result is:
105030215.0000
Which function should I use to convert this value into a DATETIME?
addTime: 12/04/2016 13:06
closeTime: 12/05/2017 16:08
Result that I am looking for (which I calculated in Excel):
29:03:02:15 (DD:HH:MM:SS)
Subtracting DATETIMEs in MySQL does not appear to give the difference between them in seconds. With your two example dates, 2016-04-12 13:06 and 2016-05-12 16:08 (assuming the 2017 in your question is a typo), subtracting them returns 100030200 whereas the correct answer is 2602920. This is out by a factor of 38.4 and a bit.
The MySQL function to return the difference in seconds is TIMESTAMPDIFF and, indeed, SELECT TIMESTAMPDIFF(SECOND, '2016-04-12 13:06', '2016-05-12 16:08') returns the correct number of seconds.
Now all we need to do is convert it into your format. MySQL has a built-in function SEC_TO_TIME which sort of does this, but unfortunately it doesn't work for periods of more than 839 hours and it doesn't itemise the days separately. We can work around this by calculating the days separately from the rest of the calculation and using SEC_TO_TIME on the leftovers once the days have been subtracted from the difference.
SELECT CONCAT(
TIMESTAMPDIFF(DAY, '2016-04-12 13:06', '2016-05-12 16:08'), ':',
SEC_TO_TIME(TIMESTAMPDIFF(SECOND, '2016-04-12 13:06', '2016-05-12 16:08') % 86400));
-- 30:03:02:00 (DD:HH:MM:SS)
This appears to be the correct answer. Although your desired output was 29:03:02:15, there were 30 days in April, and no seconds in the inputs at all.
As you're using an aggregate function to determine the number of seconds, we won't be able to select the value in days separately from the value in seconds (otherwise we'd get the average number of days followed by part of the average number of seconds), so you'll have to rewrite this as
SELECT CONCAT(
FLOOR(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) / 86400), ':',
SEC_TO_TIME(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) % 86400))
FROM deals WHERE dealid = 123;
-- 30:03:02:00.0000
This now includes fractions of a second because the average difference might not be a round number of seconds. To exclude them, use FLOOR or ROUND.
SELECT CONCAT(
FLOOR(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime)) / 86400), ':',
SEC_TO_TIME(ROUND(AVG(TIMESTAMPDIFF(SECOND, createTime, closeTime))) % 86400))
FROM deals WHERE dealid = 123;

Query to Select where timestamp between date1 and date 2

I have MySQL table
id product p_image
1 G images\20131030164545.jpg
2 S images\20131230164545.jpg
3 V images\20140110164545.jpg
4 R images\20140320164545.jpg
5 K images\20140526164545.jpg
6 L images\20150110164545.jpg
7 SK images\20150120164545.jpg
Here I need to extract products from above table where p_image timestamp between two dates (for example I need to extract from 2013/12/01 to 2014/07/30 dates)
In this query I need to extract timestamp from this string 'images\20140526164545.jpg' and convert this to date format and select values between two dates.
Assuming the format of the string is fixed (which it looks to be) you can use thesubstrfunction to extract the timestamp and then cast it to a date and filter by it. Something like this should work:
select * from table1
where cast(substr(p_image FROM 7 FOR 14) as date)
between '2013/12/01' and '2014/07/30'
Sample SQL Fiddle
There might be more efficient ways to do this, but this should give you an idea to start with.
Edit: if the string can vary the something like left(right(p_image, 18), 14) should work.
The dates in p_image are in YYYYMMDD format, so you can compare them as strings. That is, there is no reason to convert the strings to a date data type.
Hence you can just do:
where substr(p_image, 8, 8) between '20131201' and '20140730'
If the position of the date is not fixed but always after the /, you can do:
where left(substring_index(p_image, '/', -1), 8) between '20131201' and '20140730'
Try this it will working :
select * from `datetest` where substr(p_image, 8, 8) between '20131201' and '20140730'
Screenshot of Phpmyadmin :

TSQL SELECT based on a condition

I am trying to do a select from CTE based on a condition.
There is a variable I've declared for today's period (#PRD). It holds the value of what period we are currently in.
Now I would like to do a selection from a table that will restrict what information is returned based on whether we are in the first half of the year or not.
For instance, we are in period 2 so I want everything returned from my CTE which falls between PRD 1 and 5. IF we were in say period 6 (after 5), then yes I'd want everything returned from the table.
This is the pseudocode of what I'm trying to accomplish:
SELECT
CASE
WHEN #PRD <= 5
THEN (SELECT * FROM DISPLAY WHERE PERIOD IN (1,2,3,4,5))
ELSE (SELECT * FROM DISPLAY)
END
I'm getting an error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Please any thoughts on how I can do this?
Thanks x
EDITED/UPDATED:
More of the code involves a CTE and is really long. Bottom line is lets say I have this CTE
;WITH DISPLAY as (
select * from lots_of_things
)
SELECT * FROM DISPLAY
Having done a regular select on this CTE, it returns data that looks like this:
PERIOD (INT) DEPARTMENT GROUP BUDGET
1 ENERGY HE 500
2 ENERGY HE 780
3 ENERGY HE 1500
4 ENERGY HE 4500
5 ENERGY HE 400
6 ENERGY HE 3500
7 ENERGY HE 940
8 ENERGY HE 1200
I want it to show me just the top 5 rows if we the current period is 1,2,3,4,5. But to display ALL table rows if we are in any other period like 6,7,8,9 and onwards. The current period is held in the variable #PRD which is derived from doing a comparison of today's date with ranges held in a table. The value is accurate and also type INT
Hope this helps
SQL FIDDLE
This will work:
SELECT * FROM DISPLAY WHERE (#PRD > 5 OR PERIOD IN (1, 2, 3, 4, 5))
If this code confuses you, what's happening is that we check if #PRD > 5 and if that returns true, our expression is always true so we return all the rows.
If the variable is less or equal to 5 (like you checked in your example), the first check is false and then we check if the period is the list.
This might be a solution:
IF #PRD <= 5
SELECT * FROM DISPLAY WHERE PERIOD IN (1,2,3,4,5)
ELSE
SELECT * FROM DISPLAY
UPD
In this case you should use variable instead of CTE, if it's possible.
DECLARE #PRD INT;
SELECT #PRD = PERIOD FROM SOME_TABLE WHERE ...