Cast SQL Column definition and default value - sql-server

I have the following table create definition in SQL Server:
CREATE TABLE [dbo].[EventLog]
(
[RawEvent] NVARCHAR(MAX) NOT NULL,
[ReceivedDate] AS CAST(JSON_VALUE(RawEvent, '$.ReceivedTime') AS DATETIME)
)
When I receive a JSON string, that string has ReceivedTime which I parse out and use that as the ReceivedDate column's value when inserting this record.
This is currently working. However, when RawEvent string doesn't have the property ReceivedTime, I want to use the current SQL date time as the default value instead of NULL which is what it is doing now.
Is this possible to do in the table definition?

In SQL you can use COALESCE to supply a default value instead of null
COALESCE(CAST(JSON_VALUE(RawEvent, '$.ReceivedTime') AS DATETIME),GETDATE())

While I think #AaronBertrand idea of COALESCE is more straightforward, here's a working example using computed columns:
CREATE TABLE [dbo].[EventLog] (
[RawEvent] NVARCHAR(MAX) NOT NULL,
[ReceivedDate] AS CAST(JSON_VALUE(RawEvent, '$.ReceivedTime') AS DATETIME),
[ReceivedDateDefault] DATETIME NOT NULL DEFAULT GETDATE(),
[ReceivedDateCalculated] AS (CASE WHEN JSON_VALUE(RawEvent, '$.ReceivedTime') IS NULL
THEN [ReceivedDateDefault]
ELSE JSON_VALUE(RawEvent, '$.ReceivedTime') END),
)
INSERT INTO dbo.EventLog (RawEvent)
VALUES
( N'{"Foo":"Bar",
"ReceivedTime":"2019-08-02"
}'),
( N'{"Foo":"Baz"
}')
And the results:
SELECT ReceivedDateCalculated FROM dbo.EventLog
ReceivedDateCalculated
2019-08-02 00:00:00.000
2019-08-02 13:51:14.910
Might be some funky edge cases with empty strings or something, you would definitely want to consider that. There is one additional benefit of having the second date column, that you will always have a timestamp on this row (if you don't already). Which I've found tends to be pretty useful.

Related

How would one create a DATE column that is updated via a DATETIME column

Consider the following:
CREATE TABLE mytable
(
[id] INT NOT NULL,
[foobar] VARCHAR(25) NULL,
[created_on] DATETIME NOT NULL
);
SELECT *
FROM mytable
WHERE CAST(created_on AS DATE) = '2019-01-01';
I have a lot of queries like this, where I need to store the full date and time for audit (and sorting) purposes, but most queries only care about the date portion when it comes to searching.
In order to improve performance, I was considering adding a sister column that stores the value as a DATE, and then update it via triggers; but before I go down that rabbit hole, I wanted to know if there is a better way to solve this issue. Is there some mechanism in SQL Server that offers a better solution to this issue?
I am currently stuck on SQL Server 2008, but I am open to solutions that use newer versions
My preference would be to just write a sargable
WHERE created_on >= '2019-01-01' and created_on < '2019-01-02';
The
CAST(created_on AS DATE) = '2019-01-01';
Is in fact mostly sargable but somewhat sub optimal ...
... and splitting it out into a separate indexed column can help other cases like GROUP BY date
If you decide you do need a separate column you can create a computed column and index that.
This is preferable to triggers as it has less performance overhead as well as allowing SQL Server to match both the column name and the original expression. (any index on a column populated by a trigger won't be matched to a query containing CAST(created_on AS DATE))
CREATE TABLE mytable
(
[id] INT NOT NULL,
[foobar] VARCHAR(25) NULL,
[created_on] DATETIME NOT NULL,
[created_on_date] AS CAST(created_on AS DATE)
);
CREATE INDEX ix_created_on_date
ON mytable(created_on_date)
include (foobar, id, created_on)
SELECT foobar,
id,
created_on
FROM mytable
WHERE CAST(created_on AS DATE) = '2019-01-01';

Automatic adding current time in SQL Server 2012

I have table with a column of datatype time(4).
When I'm inserting values into the table I need to auto insert just the current time with milliseconds (without date) into that column. I have tried with time stamp, date time... but without any success.
If you want to get the current time in SQL Server 2012, just use the CAST operator to achieve this:
SELECT
CAST(SYSDATETIME() AS TIME(4))
This will get the current date & time, and cast this to just the time, as you need it.
To achieve automatic entry of time values when a new row is added, you can use the above expression in a default constraint. For example:
DECLARE #T AS table
(
SomeValue integer NOT NULL,
TheTime time(4) NOT NULL
DEFAULT CAST(SYSDATETIME() AS time(4))
);
INSERT #T (SomeValue)
VALUES (1);
SELECT
SomeValue,
TheTime
FROM #T;
As far as I understand, this is what you may be looking for:
SELECT GETDATE() 'Today',
CONVERT(TIME(4),GETDATE()) 'time_only'
You could use the above conversion in your insert statement as following:
INSERT INTO TABLEA (
column1
,column2
,record_insert_time
)
VALUES (
value1
,value2
,CONVERT(TIME(4), GETDATE())
);

Equivalent of GETDATE() BETWEEN Two DateTime Fields where Both can be NULL

Is
SELECT [Id]
,[DateOnline] --Nullable
,[DateOffline] --Nullable
,[PageId]
,[DownloadId]
,[Weight]
FROM [DownloadPage]
WHERE GETDATE() BETWEEN [DateOnline] AND [DateOffline]
Equivalent to:
SELECT [Id]
,[DateOnline] --Nullable
,[DateOffline] --Nullable
,[PageId]
,[DownloadId]
,[Weight]
FROM [DownloadPage]
WHERE ([DateOnline] IS NULL OR [DateOnline] <= GETDATE())
AND ([DateOffline] IS NULL OR [DateOffline] > GETDATE())
But with also catering for NULLs?
Or is there a more elegant way of doing it?
Where are parentheses needed here?
Thanks.
EDIT:
Both [DateOnline] AND [DateOffline] are of type DateTime
If [DateOnline] is NULL then the logic is "online now"
If [DateOffline] is NULL then the logic is "never go offline (once online)"
Sorry, I should have included this in my question to begin with.
The author's second query will net better performance even if there is no indexes on those columns. If there are indexes, that's a no brainer... using coalesce will disable the index and do a table scan instead of index seek (very bad for performance).
Even if there aren't any indexes on those columns, "is null" will return a constant... whereas, in the case of coalesce function, the NULL values will still need to be evaluated each time. If you have a table full of NULL DateOnline and DateOffline, this performance leak cannot be ignored.
In any case, I can't think of a reason why you'd use coalesce in this case.
Also, I'm guessing (since you're checking date range) that those two dates are all or nothing. You probably only have to check for one of those dates.
WHERE ([DateOnline] IS NULL)
OR GETDATE() BETWEEN [DateOnline] AND [DateOffline]
You can use COALESCE to convert nulls into a meaningful value. In this example I've chose default values that will always be inside the valid range.
WHERE GETDATE() BETWEEN COALESCE([DateOnline] , '1900-01-01')
AND COALESCE([DateOffline], '2099-12-31')
Demo: http://www.sqlfiddle.com/#!3/def09/1

How can I drive a boolean field in SQL Server off of a nullable column?

I'd like to have two columns in a database, one for tracking whether or not the user has submitted something, and another for the timestamp of that submission.
How can I structure the table definition so that the state of these two columns is never inconsistent?
Basically, I'd like the boolean field to be driven by whether or not a SubmittedDate column is null. Here's a snippet of the table definition:
CREATE TABLE SomeSchema.SomeTable
(
...
SubmittedDate datetime NULL,
Submitted bit NOT NULL DEFAULT(0), -- Drive off of SubmittedDate?
...
)
What's the best way to accomplish this?
Thanks!
Use only one column - the DATETIME one. It serves double duty - the column being null means it wasn't submitted, but if the value exists - you also know when.
Use a computed column:
CREATE TABLE SomeSchema.SomeTable
(
...
SubmittedDate datetime NULL,
Submitted as cast(case when SubmittedDate is null then 0 else 1 end as bit)
)

Select All as default value for Multivalue parameter

I'm building a report in Visual Studio 2008 with a lot of multivalue parameters and it's working great, but I would like to have have the "(Select all)" option as the default value when the report is opened.
Is there some kind of expression or SQL code I can use to make this happen? Or do I need to choose "(Select all)" every time, in every parameter, each time I want to run the report?
Try setting the parameters' "default value" to use the same query as the "available values". In effect it provides every single "available value" as a "default value" and the "Select All" option is automatically checked.
Using dataset with default values is one way, but you must use query for Available values and for Default Values, if values are hard coded in Available values tab, then you must define default values as expressions. Pictures should explain everything
Create Parameter (if not automaticly created)
Define values - wrong way example
Define values - correct way example
Set default values - you must define all default values reflecting available values to make "Select All" by default, if you won't define all only those defined will be selected by default.
The Result
One picture for Data type: Int
Does not work if you have nulls.
You can get around this by modifying your select statement to plop something into nulls:
phonenumber = CASE
WHEN (isnull(phonenumber, '')='') THEN '(blank)'
ELSE phonenumber
END
The accepted answer is correct, but not complete.
In order for Select All to be the default option, the Available Values dataset must contain at least 2 columns: value and label. They can return the same data, but their names have to be different. The Default Values dataset will then use value column and then Select All will be the default value. If the dataset returns only 1 column, only the last record's value will be selected in the drop down of the parameter.
Adding to the answer from E_8.
This does not work if you have empty strings.
You can get around this by modifying your select statement in SQL or modifying your query in the SSRS dataset.
Select distinct phonenumber
from YourTable
where phonenumber <> ''
Order by Phonenumber
It works better
CREATE TABLE [dbo].[T_Status](
[Status] [nvarchar](20) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[T_Status] ([Status]) VALUES (N'Active')
GO
INSERT [dbo].[T_Status] ([Status]) VALUES (N'notActive')
GO
INSERT [dbo].[T_Status] ([Status]) VALUES (N'Active')
GO
DECLARE #GetStatus nvarchar(20) = null
--DECLARE #GetStatus nvarchar(20) = 'Active'
SELECT [Status]
FROM [T_Status]
WHERE [Status] = CASE WHEN (isnull(#GetStatus, '')='') THEN [Status]
ELSE #GetStatus END
This is rather easy to achieve by making a dataset with a text-query like this:
SELECT 'Item1'
UNION
SELECT 'Item2'
UNION
SELECT 'Item3'
UNION
SELECT 'Item4'
UNION
SELECT 'ItemN'
The query should return all items that can be selected.

Resources