Update in sql server 2012 only time part of a date - date

Is there a way to perform an update in a sql server 2012 database but only of the time part?
I have this data:
id | date
1 | 2013-09-01 15:25:58.526
2 | 2013-05-10 12:12:34.345
3 | 2013-05-10 11:23:33.234
4 | 2013-04-07 15:34:01.345
And i want to change all the times to 00:00:00.000... how can i do that?

UPDATE YOUR_TABLE
SET date = CAST(
FLOOR(
CAST( date AS FLOAT )
) AS DATETIME
)

You can try
UPDATE Table1 SET [date] = CAST([date] as DATE)
SQL Fiddle DEMO
Another option would be something like
UPDATE Table1 SET [date] = DATEADD(dd, 0, DATEDIFF(dd, 0, [date]));
SQL Fiddle DEMO

Related

DATE ADD function in PostgreSQL

I currently have the following code in Microsoft SQL Server to get users that viewed on two days in a row.
WITH uservideoviewvideo (date, user_id) AS (
SELECT DISTINCT date, user_id
FROM clickstream_videos
WHERE event_name ='video_play'
and user_id IS NOT NULL
)
SELECT currentday.date AS date,
COUNT(currentday.user_id) AS users_view_videos,
COUNT(nextday.user_id) AS users_view_next_day
FROM userviewvideo currentday
LEFT JOIN userviewvideo nextday
ON currentday.user_id = nextday.user_id AND DATEADD(DAY, 1,
currentday.date) = nextday.date
GROUP BY currentday.date
I am trying to get the DATEADD function to work in PostgreSQL but I've been unable to figure out how to get this to work. Any suggestions?
I don't think PostgreSQL really has a DATEADD function. Instead, just do:
+ INTERVAL '1 day'
SQL Server:
Add 1 day to the current date November 21, 2012
SELECT DATEADD(day, 1, GETDATE()); # 2012-11-22 17:22:01.423
PostgreSQL:
Add 1 day to the current date November 21, 2012
SELECT CURRENT_DATE + INTERVAL '1 day'; # 2012-11-22 17:22:01
SELECT CURRENT_DATE + 1; # 2012-11-22 17:22:01
http://www.sqlines.com/postgresql/how-to/dateadd
EDIT:
It might be useful if you're using a dynamic length of time to create a string and then cast it as an interval like:
+ (col_days || ' days')::interval
You can use date + 1 to do the equivalent of dateadd(), but I do not think that your query does what you want to do.
You should use window functions, instead:
with plays as (
select distinct date, user_id
from clickstream_videos
where event_name = 'video_play'
and user_id is not null
), nextdaywatch as (
select date, user_id,
case
when lead(date) over (partition by user_id
order by date) = date + 1 then 1
else 0
end as user_view_next_day
from plays
)
select date,
count(*) as users_view_videos,
sum(user_view_next_day) as users_view_next_day
from nextdaywatch
group by date
order by date;

Generate Multiple rows from single row depending on date difference

I want to generate multiple rows from 1 rows depending on dates difference in dtStart and dtEnd
-- This is demo table to show the issue
create table #temp(Id int,hTenant int , dtStart datetime,dtEnd datetime)
Insert into #temp values(1,8,'2013-01-08 00:00:00.000','2014-01-01 00:00:00.000')
And data should be returned by query as :-
**Id** **Tenant** **Month** **Year**
1 8 Aug 2013
1 8 Sep 2013
1 8 Oct 2013
1 8 Nov 2013
1 8 Dec 2013
1 8 Jan 2014
How i can achieve this
I have created one table valued function which returns month and year but not able to join it with the table to fetch id and tenant
Create FUNCTION vw_emg_common_GetYearMonthDiffList ( #startdt datetime,#enddt datetime )
RETURNS #Months TABLE
(
Months int,
Years int,
StartDate datetime,
EndDate datetime
)
AS
BEGIN
WHILE (#startdt< #enddt)
BEGIN
INSERT INTO #Months(Months,Years,StartDate,EndDate) VALUES (MONTH(#startdt),Year(#startdt),#startdt,#enddt)
SET #startdt = DATEADD(MONTH,1,#startdt)
end
INSERT #Months
Select Months,Years,StartDate,EndDate from #Months
RETURN
end
Function call :-
select * FROM vw_emg_common_GetYearMonthDiffList('2013-01-01 00:00:00.000','2013-12-01 00:00:00.000' )
Try this method using Cross Join and CTE. You may need to create a function (or slightly modify). Fiddle demo is here
DECLARE #sd DATE = '20130801', #ed DATE = '20140101',
#id INT = 1, #hTenant INT = 8
;WITH CTE AS
(
SELECT DATEDIFF(MONTH, #sd, #ed) Months
)
SELECT DISTINCT #id Id, #hTenant Tenant,
DATENAME(MONTH, DATEADD(MONTH, number, #sd)) [Month],
YEAR(DATEADD(MONTH, number, #sd)) [Year]
FROM master..spt_values x
CROSS JOIN CTE
WHERE x.number BETWEEN 0 AND Months
Results
| ID | TENANT | MONTH | YEAR |
|----|--------|-----------|------|
| 1 | 8 | August | 2013 |
| 1 | 8 | September | 2013 |
| 1 | 8 | October | 2013 |
| 1 | 8 | November | 2013 |
| 1 | 8 | December | 2013 |
| 1 | 8 | January | 2014 |
Hope this helps you.
DECLARE #TEMP TABLE(ID INT,TENANT INT , DTSTART DATETIME,DTEND DATETIME)
INSERT INTO #TEMP VALUES(1,8,'2013-01-08 00:00:00.000','2014-01-01 00:00:00.000')
--You can create a function with the given logic
--It receives STARTDATE and ENDDATE
DECLARE #STARTDATE DATETIME = (SELECT DTSTART FROM #TEMP),
#ENDDATE DATETIME = (SELECT DTEND FROM #TEMP)
DECLARE #S INT = CAST(#STARTDATE AS INT)
DECLARE #E INT = CAST(#ENDDATE AS INT)
DECLARE #TAB TABLE (ID INT, DT DATETIME)
WHILE #S <= #E
BEGIN
INSERT INTO #TAB VALUES (#S,CAST(#S AS DATETIME))
SET #S = #S + 1
END
SELECT TEMP.ID,TEMP.TENANT,[Mname] MONTH,[Y] YEAR FROM (
SELECT DATEPART(YEAR,DT) [Y],DATEPART(MONTH,DT) [Mnum],DATENAME(MONTH,DT) [Mname] FROM #TAB
GROUP BY DATEPART(YEAR,DT),DATEPART(MONTH,DT),DATENAME(MONTH,DT)) LU,#TEMP TEMP
ORDER BY [Y],[Mnum]
Result:

Convert HH:MM:SS string to number of minutes

I have the below query.
select cast(dateadd(minute, datediff(minute, TimeIn, TimeOut), 0) as time(0) )
I get the results from two columns in the format of hrs-min-seconds.
I would like it in the format of min only. So 02:47:00 will read 167.
SQL Server Query:
SELECT cast(substring('02:47:00',1,2) AS int)*60+
cast(substring('02:47:00',4,2) AS int)+
cast(substring('02:47:00',7,2) AS int)/60.0 AS minutes
MYSQL Query:
SELECT TIME_TO_SEC('02:47:00') / 60
Result:
| MINUTES |
-----------
| 167 |
declare #Time DATETIME = '01:05:00'
select ((DATEPART(HOUR, #Time)*60) + (DATEPART(MINUTE, #Time)))
For SQL Server (works for 2005 too):
select Datediff(mi,convert(datetime,'00:00:00',108), convert(datetime,'02:47:00',108))
Try this:
datediff(minute, 0, '02:47')
Expanding on Justin's answer. This allows for situations where hours is larger than 2 digits.
declare #time varchar(50) = '102:47:05'
SELECT cast(right(#time,2) AS int)+
cast(left(right(#time,5),2) AS int)*60+
cast(left(#time,len(#time)-6) AS int)*3600 AS seconds,
(cast(right(#time,2) AS int)+
cast(left(right(#time,5),2) AS int)*60+
cast(left(#time,len(#time)-6) AS int)*3600)/60.0 AS minutes
Result:
seconds minutes
----------- ---------------------------------------
370025 6167.083333
SELECT DATEDIFF(minute,CAST('00:00' AS TIME), CAST('02:47' AS TIME)) AS difference
Gives you:
| DIFFERENCE |
--------------
| 167 |
Unfortunately, if you want to use DATEPART function for values with more than 24 hours, you will receive an error:
Conversion failed when converting date and/or time from character string."
You can test it with this code:
declare #Time DATETIME = '32:00:00'
select ((DATEPART(HOUR, #Time)*60) + (DATEPART(MINUTE, #Time)))
To solve this, I worked with this another approach:
declare #tbl table(WorkHrs VARCHAR(8))
insert into #tbl(WorkHrs) values ('02:47:00')
insert into #tbl(WorkHrs) values ('32:00:00')
-- Sum in minutes
SELECT TRY_CAST(([HOURS] * 60) + [MINUTES] + ([SECOND] / 60) AS INT) as TotalInMinutes
FROM (
SELECT
-- Use this aproach to get separated values
SUBSTRING(WorkHrs,1,CHARINDEX(':',WorkHrs)-1) AS [HOURS],
SUBSTRING(WorkHrs,4,CHARINDEX(':',WorkHrs)-1) AS [MINUTES],
SUBSTRING(WorkHrs,7,CHARINDEX(':',WorkHrs)-1) AS [SECOND] -- probably you can ignore this one
FROM #tbl
)
tbl
-- Sum in seconds
SELECT TRY_CAST(([HOURS] * 3600) + ([MINUTES] * 60) + [SECOND] AS INT) as TotalInSeconds
FROM (
SELECT
-- Use this aproach to get separated values
SUBSTRING(WorkHrs,1,CHARINDEX(':',WorkHrs)-1) AS [HOURS],
SUBSTRING(WorkHrs,4,CHARINDEX(':',WorkHrs)-1) AS [MINUTES],
SUBSTRING(WorkHrs,7,CHARINDEX(':',WorkHrs)-1) AS [SECOND]
FROM #tbl
)
tbl
This code will return like this:
$time = '02:47:00';
$time = explode(":",$time);
$total = ($a[0]*60)+$a[1];
echo 'Minutes : '.$total;<br>
echo 'Seconds : '.$a[2];

How can i select all dates between date range?

I have one SQL Table with 2 columns as below
Column1: ProductionDate - DateTime - Not NULL
Column2: Quantity - Int - Not NULL
Now There are 2 Records in Table
1-1-2012, 5
1-3-2012, 7
Output of Result should be as below if i give date range StartDate as 1-1-2012 and EndDate as 1-15-2012
1-1-2012 5
1-2-2012 0
1-3-2012 7
1-4-2012 0
1-5-2012 0
1-6-2012 0
.
.
.
1-15-2012 0
Means Query should return all the dates of given range with Quantity and if no entry in Table then 0 for Quantity.
How to Do it? Please suggest with Query
Here's one very optimistic draft on what you can use ( source - here )
declare #startDate datetime;
declare #endDate datetime;
set #startDate = '2012-02-09';
set #endDate = '2012-02-15';
WITH span AS (
SELECT #startDate AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM span s
WHERE DATEADD(dd, 1, dt) <= #endDate)
select s.dt, t.Quantity from span s
join table t
on s.dt = t.ProductionDate

SQL query to convert date ranges to per day records

Requirements
I have data table that saves data in date ranges.
Each record is allowed to overlap previous record(s) (record has a CreatedOn datetime column).
New record can define it's own date range if it needs to hence can overlap several older records.
Each new overlapping record overrides settings of older records that it overlaps.
Result set
What I need to get is get per day data for any date range that uses record overlapping. It should return a record per day with corresponding data for that particular day.
To convert ranges to days I was thinking of numbers/dates table and user defined function (UDF) to get data for each day in the range but I wonder whether there's any other (as in better* or even faster) way of doing this since I'm using the latest SQL Server 2008 R2.
Stored data
Imagine my stored data looks like this
ID | RangeFrom | RangeTo | Starts | Ends | CreatedOn (not providing data)
---|-----------|----------|--------|-------|-----------
1 | 20110101 | 20110331 | 07:00 | 15:00
2 | 20110401 | 20110531 | 08:00 | 16:00
3 | 20110301 | 20110430 | 06:00 | 14:00 <- overrides both partially
Results
If I wanted to get data from 1st January 2011 to 31st May 2001 resulting table should look like the following (omitted obvious rows):
DayDate | Starts | Ends
--------|--------|------
20110101| 07:00 | 15:00 <- defined by record ID = 1
20110102| 07:00 | 15:00 <- defined by record ID = 1
... many rows omitted for obvious reasons
20110301| 06:00 | 14:00 <- defined by record ID = 3
20110302| 06:00 | 14:00 <- defined by record ID = 3
... many rows omitted for obvious reasons
20110501| 08:00 | 16:00 <- defined by record ID = 2
20110502| 08:00 | 16:00 <- defined by record ID = 2
... many rows omitted for obvious reasons
20110531| 08:00 | 16:00 <- defined by record ID = 2
Actually, since you are working with dates, a Calendar table would be more helpful.
Declare #StartDate date
Declare #EndDate date
;With Calendar As
(
Select #StartDate As [Date]
Union All
Select DateAdd(d,1,[Date])
From Calendar
Where [Date] < #EndDate
)
Select ...
From Calendar
Left Join MyTable
On Calendar.[Date] Between MyTable.Start And MyTable.End
Option ( Maxrecursion 0 );
Addition
Missed the part about the trumping rule in your original post:
Set DateFormat MDY;
Declare #StartDate date = '20110101';
Declare #EndDate date = '20110501';
-- This first CTE is obviously to represent
-- the source table
With SampleData As
(
Select 1 As Id
, Cast('20110101' As date) As RangeFrom
, Cast('20110331' As date) As RangeTo
, Cast('07:00' As time) As Starts
, Cast('15:00' As time) As Ends
, CURRENT_TIMESTAMP As CreatedOn
Union All Select 2, '20110401', '20110531', '08:00', '16:00', DateAdd(s,1,CURRENT_TIMESTAMP )
Union All Select 3, '20110301', '20110430', '06:00', '14:00', DateAdd(s,2,CURRENT_TIMESTAMP )
)
, Calendar As
(
Select #StartDate As [Date]
Union All
Select DateAdd(d,1,[Date])
From Calendar
Where [Date] < #EndDate
)
, RankedData As
(
Select C.[Date]
, S.Id
, S.RangeFrom, S.RangeTo, S.Starts, S.Ends
, Row_Number() Over( Partition By C.[Date] Order By S.CreatedOn Desc ) As Num
From Calendar As C
Join SampleData As S
On C.[Date] Between S.RangeFrom And S.RangeTo
)
Select [Date], Id, RangeFrom, RangeTo, Starts, Ends
From RankedData
Where Num = 1
Option ( Maxrecursion 0 );
In short, I rank all the sample data preferring the newer rows that overlap the same date.
Why do it all in DB when you can do it better in memory
This is the solution (I eventually used) that seemed most reasonable in terms of data transferred, speed and resources.
get actual range definitions from DB to mid tier (smaller amount of data)
generate in memory calendar of a certain date range (faster than in DB)
put those DB definitions in (much easier and faster than DB)
And that's it. I realised that complicating certain things in DB is not not worth it when you have executable in memory code that can do the same manipulation faster and more efficient.