SQL Server: if "datetime2" < 1753, then "datetime" NULL? - sql

My intention is to insert data from source table into target table. Source table has datetime2 column and target table has datetime column. If datetime2 value does not fit (< year 1753) into datetime field, it will be converted to null. Here is an example
DROP TABLE dbo.test1
--source table
CREATE TABLE dbo.test1 (wday DATETIME2 NULL)
go
INSERT INTO dbo.test1
(wday
)
SELECT '2008-02-01 00:00:00.000'
UNION ALL
SELECT '2009-02-01 00:00:00.000'
UNION ALL
SELECT '0001-02-01 00:00:00.000'
DROP TABLE dbo.test2
--target table
CREATE TABLE dbo.test2 (wday DATETIME NULL)
go
--insert only valid datetime dates, < 1753 will be converted to nulls
INSERT INTO dbo.test2
(wday
)
SELECT CASE WHEN DATEDIFF(YEAR, dbo.test1.wday, GETDATE()) < 111
THEN NULL
ELSE CAST(dbo.test1.wday AS DATETIME)
END
FROM dbo.test1
The code does not work. Also using datediff here is not valid logic, how to implement this?

why not just
INSERT dbo.test2
(wday
)
SELECT CASE WHEN dbo.test1.wday < '17530101'
THEN NULL
ELSE CAST(dbo.test1.wday AS DATETIME)
END
FROM dbo.test1

Another option:
--insert only valid datetime dates, < 1753 will be converted to nulls
INSERT INTO dbo.test2
(wday
)
SELECT CASE WHEN DATEPART(YEAR, dbo.test1.wday) < 1753
THEN NULL
ELSE CAST(dbo.test1.wday AS DATETIME)
END
FROM dbo.test1

Related

SQL query to select dates between two dates and exclude dates between two dates

CREATE TABLE [dbo].[#temp1]
(
[workDate] [datetime] NULL,
[Id] [int] NULL,
) ON [PRIMARY]
INSERT INTO [dbo].[#temp1]
VALUES ('12-01-2018', '11'), ('11-01-2018', '11'),
('10-01-2018', '11'), ('09-01-2018', '11')
CREATE TABLE [dbo].[#temp2]
(
[workDate] [datetime] NULL,
[Id] [int] NULL,
) ON [PRIMARY]
INSERT INTO [dbo].[#temp2]
VALUES ('10-01-2018', '11'), ('09-01-2018', '11')
I have 2 tables with dates.
I want to select all the dates from #temp1 but do not count the dates from #temp2.
I used, but did not get the desired result:
select A.workDate, A.Id
from [dbo].[#temp1] A
left join [dbo].[#temp2] B on A.Id = B.Id and A.workDate = B.workDate
where A.workDate between CAST('09.01.2018' as datetime) and CAST('12.01.2018' as datetime)
or B.workDate not between CAST('09.01.2018' as datetime) and CAST('10.01.2018' as datetime)
The result I want to get:
workDate Id
-------------------------------
2018-01-12 00:00:00.000 11
2018-01-11 00:00:00.000 11
How to fix it?
If you want to select rows having dates between two date values and which are not in an other table, first select the rows using Between .. and in a Where clause and omit the dates which are part of other table using Not Exists.
Query
select * from [dbo].[#temp1] as [t1]
where cast([workDate] as date) between '2018-09-01' and '2018-12-01'
and not exists(
select 1 from [dbo].[#temp2] as [t2]
where [t1].[workDate] = [t2].[workDate]
);
The way you wrote the query depends on the values in the tables. If you already know the dates you want to select, you can just do this:
where A.workDate between CAST('11.01.2018' as datetime) and CAST('12.01.2018' as datetime)
What you probably want instead is this, to select rows where the left join does not match anything:
where B.workDate is null
Update your query with and condition as below. And check that B.workDate IS NULL so that is record doesn't exist in [dbo].[#temp2] then also it will give the result.
select A.workDate, A.Id
from [dbo].[#temp1] A
left join [dbo].[#temp2] B on A.Id = B.Id and A.workDate = B.workDate
where A.workDate between CAST('09.01.2018' as datetime) and CAST('12.01.2018' as datetime)
and (B.workDate IS NULL or B.workDate not between CAST('09.01.2018' as datetime) and CAST('10.01.2018' as datetime)
A.Id=B.Id - this condition in join predicate is logically wrong/makes no sense. You were talking about comparing dates - what would Ids be here for? Same thing about OR part in where.
As you stated your job: dates from #temp1 except dates from #temp2 - it can be "translated" from english into SQL almost word to word:
select A.workDate
from [dbo].[#temp1] A
where A.workDate between CAST('09.01.2018' as datetime) and CAST('12.01.2018' as datetime)
EXCEPT
select B.workData
from [dbo].[#temp2] B
or "dates from #temp1 which do not exist in #temp2":
select A.workDate,A.Id
from [dbo].[#temp1] A
where A.workDate between CAST('09.01.2018' as datetime) and CAST('12.01.2018' as datetime)
AND not exists(select 1 from [dbo].[#temp2] B WHERE B.workDate = A.workDate)
Yes, that simple.
Also you better don't cast dates like that: CAST('09.01.2018' as datetime). "Default" date format depends on settings and can be, e.g.
ddmmyy German
yymmdd ANSI
mmddyy USA
So this cast may result different dates (month 09 day 01 or month 01 day 09?)
Use CONVERT(datetime, '09.01.2018', 104). And better tend not to use literals and magic strings/numbers within a query - put those values into variables and use them like that:
declare #date_begin date = CONVERT(date, '20180109', 112),
#date_end date = CONVERT(date, '20180112', 112)
select A.workDate,A.Id
from [dbo].[#temp1] A
where A.workDate between #date_begin and #date_end
AND not exists(select 1 from [dbo].[#temp2] B WHERE B.workDate = A.workDate)

SQL Server: Populate (Minute) Date Dimension table

I'm working on a script to populate a very simple date dimension table whose granularity is down to the minute level. This table should ultimately contain a smalldatetime representing every minute from 1/1/2000 to 12/31/2015 23:59.
Here is the definition for the table:
CREATE TABLE [dbo].[REF_MinuteDimension] (
[TimeStamp] SMALLDATETIME NOT NULL,
CONSTRAINT [PK_REF_MinuteDimension] PRIMARY KEY CLUSTERED ([TimeStamp] ASC) WITH (FILLFACTOR = 100)
);
Here is the latest revision of the script:
DECLARE #CurrentTimeStamp AS SMALLDATETIME;
SELECT TOP(1) #CurrentTimeStamp = MAX([TimeStamp]) FROM [dbo].[REF_MinuteDimension];
IF #CurrentTimeStamp IS NOT NULL
SET #CurrentTimeStamp = DATEADD(MINUTE, 1, #CurrentTimeStamp);
ELSE
SET #CurrentTimeStamp = '1/1/2000 00:00';
ALTER TABLE [dbo].[REF_MinuteDimension] DROP CONSTRAINT [PK_REF_MinuteDimension];
WHILE #CurrentTimeStamp < '12/31/2050 23:59'
BEGIN
;WITH DateIndex ([TimeStamp]) AS
(
SELECT #CurrentTimeStamp
UNION ALL
SELECT DATEADD(MINUTE, 1, [TimeStamp]) FROM DateIndex di WHERE di.[TimeStamp] < dbo.fGetYearEnd(#CurrentTimeStamp)
)
INSERT INTO [dbo].[REF_MinuteDimension] ([TimeStamp])
SELECT di.[TimeStamp] FROM DateIndex di
OPTION (MAXRECURSION 0);
SET #CurrentTimeStamp = DATEADD(YEAR, 1, dbo.fGetYearBegin(#CurrentTimeStamp))
END
ALTER TABLE [dbo].[REF_MinuteDimension] ADD CONSTRAINT [PK_REF_MinuteDimension] PRIMARY KEY CLUSTERED ([TimeStamp] ASC) WITH (FILLFACTOR = 100);
A couple of things to point out:
I've added logic to drop and subsequently re-add the primary key constraint on the table, hoping to boost the performance.
I've added logic to chunk the INSERTS into yearly batches to minimize the impact on the transaction log. On a side note, we're using the SIMPLE recovery model.
Performance is so-so and takes around 15-20 minutes to complete. Any hints/suggestions on how this script could be "tuned up" or improved?
Also, for completeness here are fGetYearBegin and fGetYearEnd:
CREATE FUNCTION dbo.fGetYearBegin
(
#dtConvertDate datetime
)
RETURNS smalldatetime
AS
BEGIN
RETURN DATEADD(YEAR, DATEDIFF(YEAR, 0, #dtConvertDate), 0)
END
CREATE FUNCTION dbo.fGetYearEnd
(
#dtConvertDate datetime
)
RETURNS smalldatetime
AS
BEGIN
RETURN DATEADD(MINUTE, -1, DATEADD(YEAR, 1, dbo.fGetYearBegin(#dtConvertDate)))
END
This takes 11 seconds on my server...
If Object_ID('tempdb..#someNumbers') Is Not Null Drop Table #someNumbers;
Create Table #someNumbers (id Int);
Declare #minutes Int,
#days Int;
Select #minutes = DateDiff(Minute,'1/1/2000 00:00','1/2/2000 00:00'),
#days = DateDiff(Day,'1/1/2000 00:00','1/1/2051 00:00');
With Base As
(
Select 1 As seedID
Union All
Select 1
), Build As
(
Select seedID
From Base
Union All
Select b.seedID + 1
From Build b
Cross Join Base b2
Where b.SeedID < 14
)
Insert #someNumbers
Select Row_Number() Over (Order By seedID) As id
From Build
Option (MaxRecursion 0);
If Object_ID('tempdb..#values') Is Not Null Drop Table #values;
Create Table #values ([TimeStamp] SmallDateTime NOT NULL);
With Dates As
(
Select DateAdd(Day,id-1,'1/1/2000 00:00') As [TimeStamp]
From #someNumbers
Where id <= #days
)
Insert #values
Select Convert(SmallDateTime,DateAdd(Minute,id-1,[TimeStamp]))
From Dates d
Join #someNumbers sn
On sn.id <= #minutes
Order By 1

SQL Server 2012 Insert DATEDIFF into column trigger whenever a new record is inserted

Table
CREATE TABLE CurrentApplication
(
StartDate datetime NOT NULL,
EndDate datetime NOT NULL,
NoOfDays integer,
StaffID integer NOT NULL,
AppStatus varchar(30) NOT NULL DEFAULT 'PENDING'
)
Trigger
CREATE TRIGGER InsertNoOfDays ON CurrentApplication
AFTER INSERT
AS
BEGIN
DECLARE #temp INT
SELECT #temp = DATEDIFF(day, EndDate, StartDate)
FROM inserted
INSERT INTO CurrentApplication(NoOfDays) VALUES (#temp)
--SELECT StaffID = inserted.StaffID
--FROM inserted
-- INSERT INTO CurrentApplication(NoOfDays)
-- SELECT Datediff(day, EndDate, StartDate)
-- FROM inserted;
END
Error message:
Msg 515, Level 16, State 2, Procedure InsertNoOfDays, Line 10
Cannot insert the value NULL into column 'StartDate', table
'StaffPortalDB.dbo.CurrentApplication'; column does not allow nulls.
INSERT fails. The statement has been terminated.
What I'm trying to do is I have a table CurrentApplication and I want the NoOfDays column to automatically be populated whenever a user inserts a new row, with the date difference of start day and end day.
IF Sql server
Try inserting some default or dummy values,since its not null column
Some thing like this:
CREATE TRIGGER InsertNoOfDays ON CurrentApplication
AFTER INSERT
AS
BEGIN
DECLARE #temp INT
SELECT #temp = coalesce(DATEDIFF(day, EndDate, StartDate),0) --Default 0
FROM inserted
INSERT INTO CurrentApplication(NoOfDays) VALUES (#temp)
--SELECT StaffID = inserted.StaffID
--FROM inserted
-- INSERT INTO CurrentApplication(NoOfDays)
-- SELECT Datediff(day, EndDate, StartDate)
-- FROM inserted;
END
It's because your Insert statement is attempting to insert a record but isn't inserting any values into the columns that cannot be empty (StartDate, EndDate, StaffID, AppStatus). For this insert to succeed you need to either change the INSERT statement to insert a value into these columns or change the table schema to allow NULL values.

nvarchar column to find the previous date in sql

I have followin table schema
declare #temp table
(
id int identity(1,1) not null,
nod nvarchar(50)
)
in which nod column have following data
insert into #temp select 'N/A'
insert into #temp select 'N/A'
insert into #temp select '5'
insert into #temp select 'N/A'
insert into #temp select '7'
insert into #temp select 'N/A'
insert into #temp select '31'
insert into #temp select '15'
i want that select stament shoud give me result on following basis
if nod value 'N/A' then it should show 'N/A'
or if there any numeric value like 5,15,31 then it should show getdate()-nod date date column
I have tried following but fail to minus the days and also represent 'N/A' when 'N/A' in that nvarchar column
select DATEADD(dd,case nod when 'N/A' then 0 else nod end,GETDATE()) from #temp
sql fiddle is here
The following query would return a VARCHAR(50) column that contains either 'N/A' or now - nod date as varchar.
SELECT
CASE nod
WHEN 'N/A' THEN 'N/A'
ELSE CONVERT(VARCHAR(50),
DATEADD(dd,
-1 * CONVERT(INT, nod),
GETDATE()
)
)
END
FROM #temp

Need multiple copies of one resultset in sql without using loop

Following is the sample data. I need to make 3 copies of this data in t sql without using loop and return as one resultset. This is sample data not real.
42 South Yorkshire
43 Lancashire
44 Norfolk
Edit: I need multiple copies and I have no idea in advance that how many copies I need I have to decide this on the basis of dates. Date might be 1st jan to 3rd Jan OR 1st jan to 8th Jan.
Thanks.
Don't know about better but this is definatley more creative! you can use a CROSS JOIN.
EDIT: put some code in to generate a date range, you can change the date range, the rows in the #date are your multiplier.
declare #startdate datetime
, #enddate datetime
create table #data1 ([id] int , [name] nvarchar(100))
create table #dates ([date] datetime)
INSERT #data1 SELECT 42, 'South Yorkshire'
INSERT #data1 SELECT 43, 'Lancashire'
INSERT #data1 SELECT 44, 'Norfolk'
set #startdate = '1Jan2010'
set #enddate = '3Jan2010'
WHILE (#startdate <= #enddate)
BEGIN
INSERT #dates SELECT #startdate
set #startdate=#startdate+1
END
SELECT [id] , [name] from #data1 cross join #dates
drop table #data1
drop table #dates
You could always use a CTE to do the dirty work
Replace the WHERE Counter < 4 with the amount of duplicates you need.
CREATE TABLE City (ID INTEGER PRIMARY KEY, Name VARCHAR(32))
INSERT INTO City VALUES (42, 'South Yorkshire')
INSERT INTO City VALUES (43, 'Lancashire')
INSERT INTO City VALUES (44, 'Norfolk')
/*
The CTE duplicates every row from CTE for the amount
specified by Counter
*/
;WITH CityCTE (ID, Name, Counter) AS
(
SELECT c.ID, c.Name, 0 AS Counter
FROM City c
UNION ALL
SELECT c.ID, c.Name, Counter + 1
FROM City c
INNER JOIN CityCTE cte ON cte.ID = c.ID
WHERE Counter < 4
)
SELECT ID, Name
FROM CityCTE
ORDER BY 1, 2
DROP TABLE City
This may not be the most efficient way of doing it, but it should work.
(select ....)
union all
(select ....)
union all
(select ....)
Assume the table is named CountyPopulation:
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
Share and enjoy.
There is no need to use a cursor. The set-based approach would be to use a Calendar table. So first we make our calendar table which need only be done once and be somewhat permanent:
Create Table dbo.Calendar ( Date datetime not null Primary Key Clustered )
GO
; With Numbers As
(
Select ROW_NUMBER() OVER( ORDER BY S1.object_id ) As [Counter]
From sys.columns As s1
Cross Join sys.columns As s2
)
Insert dbo.Calendar([Date])
Select DateAdd(d, [Counter], '19000101')
From Numbers
Where [Counter] <= 100000
GO
I populated it with a 100K dates which goes into 2300. Obviously you can always expand it. Next we generate our test data:
Create Table dbo.Data(Id int not null, [Name] nvarchar(20) not null)
GO
Insert dbo.Data(Id, [Name]) Values(42,'South Yorkshire')
Insert dbo.Data(Id, [Name]) Values(43, 'Lancashire')
Insert dbo.Data(Id, [Name]) Values(44, 'Norfolk')
GO
Now the problem becomes trivial:
Declare #Start datetime
Declare #End datetime
Set #Start = '2010-01-01'
Set #End = '2010-01-03'
Select Dates.[Date], Id, [Name]
From dbo.Data
Cross Join (
Select [Date]
From dbo.Calendar
Where [Date] >= #Start
And [Date] <= #End
) As Dates
By far the best solution is CROSS JOIN. Most natural.
See my answer here: How to retrieve rows multiple times in SQL Server?
If you have a Numbers table lying around, it's even easier. You can DATEDIFF the dates to give you the filter on the Numbers table