SQL Server DDL Discrepancy between DataGrip and SSMS

I have a table in SqlServer. Using DataGrip, it generates the following DDL. Note the StopDate, StopDateFrom, and StopDateTo columns; they implement a user defined type Date_NONULLS.

create table tbOMOrderStopDate
(
Id Id not null
constraint PK_tbOMOrderStopDate
primary key nonclustered,
OrderStopId Id not null
constraint FK_tbOMOrderStopDate_tbOMOrderStop
references tbOMOrderStop,
StopDateType varchar(50) not null
constraint FK_tbOrderStopDate_tbLkupStopDateType
references tbLkupOMStopDateType (Code),
StopDate Date_NONULLS,
StopDateFrom Date_NONULLS,
StopDateTo Date_NONULLS,
CreateDate CreateDateTime not null,
CreateUserId UserId not null,
ChangeDate ChangedDateTime not null,
ChangeUserId UserId not null,
constraint CK_tbOMOrderStopDate_DuplicateArriveDeparts
check ([dbo].[fnCheckForDuplicateStopDate]([OrderStopId],[StopDateType])=0)
)

The UDT translates to "SMALLDATETIME NOT NULL".

Now, here is the same table with DDL generated by SSMS:

CREATE TABLE [dbo].[tbOMOrderStopDate](
[Id] [dbo].[Id] NOT NULL,
[OrderStopId] [dbo].[Id] NOT NULL,
[StopDateType] [varchar](50) NOT NULL,
[StopDate] [dbo].[Date_NONULLS] NULL,
[StopDateFrom] [dbo].[Date_NONULLS] NULL,
[StopDateTo] [dbo].[Date_NONULLS] NULL,
[CreateDate] [dbo].[CreateDateTime] NOT NULL,
[CreateUserId] [dbo].[UserId] NOT NULL,
[ChangeDate] [dbo].[ChangedDateTime] NOT NULL,
[ChangeUserId] [dbo].[UserId] NOT NULL,
[RowStatus] [char](1) NULL,
CONSTRAINT [PK_tbOMOrderStopDate] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 85) ON [PRIMARY]
) ON [PRIMARY]


Again, note the three columns: StopDate, StopDateFrom, and StopDateTo. Note that some Einstein implemented the Date_NONULLS udt and then overrode the value using NULL.

My point is that DataGrip didn't show that trailing NULL after the UDT. I was scratching my head wondering how it is that SqlServer was permitting NULL values in a columns defined as NOTNULL.

I think DataGrip missed something when generating the DDL,

dvn

 

1
14 comments

@don v nielsen Hi,

Could you provide original DDL for mentioned table?

0

That's as good as I got, Vasily. I don't have any change tracking or anything like that to reverse any changes. I am assuming that is the original DDL.

```

CREATE TABLE [dbo].[tbOMOrderStopDate](
    [Id] [dbo].[Id] NOT NULL,
    [OrderStopId] [dbo].[Id] NOT NULL,
    [StopDateType] [varchar](50) NOT NULL,
    [StopDate] [dbo].[Date_NONULLS] NULL,
    [StopDateFrom] [dbo].[Date_NONULLS] NULL,
    [StopDateTo] [dbo].[Date_NONULLS] NULL,
    [CreateDate] [dbo].[CreateDateTime] NOT NULL,
    [CreateUserId] [dbo].[UserId] NOT NULL,
    [ChangeDate] [dbo].[ChangedDateTime] NOT NULL,
    [ChangeUserId] [dbo].[UserId] NOT NULL,
    [RowStatus] [char](1) NULL,
    CONSTRAINT [PK_tbOMOrderStopDate] PRIMARY KEY NONCLUSTERED
    (
    [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS =     ON, FILLFACTOR = 85) ON [PRIMARY]
    ) ON [PRIMARY]

```

 

 

0

@don v nielsen Could you provide definition for UDT Date_NONULLS to reproduce the problem?

0
CREATE TYPE DATE_NONULLS FROM SMALLDATETIME NOT NULL
GO
0

@don v nielsen So how DATE_NONULLS could be NULL? Looks like there is an issue in SSMS?

0

> So how DATE_NONULLS could be NULL?

Exactly!

[StopDate] [dbo].[Date_NONULLS] NULL,
[StopDateFrom] [dbo].[Date_NONULLS] NULL,
[StopDateTo] [dbo].[Date_NONULLS] NULL,

Because SqlServer allows you to apply the UDT and then constraints that follow. The UDT says NOT NULL, but the trailing constraint NULL overrides it.

So what is happening in DataGrip is that it is picking up the UDT but not the trailing constraints. Technically, DataGrip is not creating DDL that duplicates the existing table.

UDT's suck, anyway. They are a maintenance nightmare and everyone around me hates them. But it is what it is, unfortunately.

 

 

 

0

@don v nielsen
I tried to reproduce the issue. My environment:

  • Microsoft SQL Server 2017 (RTM-CU3-GDR) (KB4052987) - 14.0.3015.40 (X64)   Dec 22 2017 16:13:22   Copyright (C) 2017 Microsoft Corporation  Developer Edition (64-bit) on Linux (Ubuntu 16.04.3 LTS)
  • SQL Server 16 Microsoft SQL Server Management Studio    13.0.15900.1
  • DataGrip 2017.3.7

I created UDT:

CREATE TYPE DATE_NONULLS FROM SMALLDATETIME NOT NULL


Table:

CREATE TABLE test_table (STOPDATE DATE_NONULLS)


Then I generate create statement with  SSMS & DG.
SSMS:

CREATE TABLE [dbo].[test_table](
    [StopDate] [dbo].[DATE_NONULLS] NOT NULL
) ON [PRIMARY]
GO


DG:

CREATE TABLE dbo.test_table
(
  StopDate DATE_NONULLS NOT NULL
)
GO

So there is no difference.

I tried to insert NULL value using SSMS in two ways. I can't insert with INSERT statement. SSMS crashes if I invoke "Edit top 200 rows...".
I can't insert NULL value with DG.

Could you describe your environment in the same manner I did?

0
You implemented above a UDT that contains NOT NULL and a trailing constraint as NOT NULL. Try the following. It implements your UDT with NOT NULL with a trailing constraint of NULL, which seems to override it.
CREATE TABLE [dbo].[test_table](
    [StopDate] [dbo].[DATE_NONULLS] NULL
) ON [PRIMARY]
GO
I have no idea what the original programmer was thinking implementing something in that manner. The consensus among us is that the programmer wasn't thinking. But it is what is, and we are stuck with it.
 
I'm less concerned about SSMS accepting nulls, and more concerned about DataGrip reproducing the DDL that reflects the tables implementation.  I just want to make sure DataGrip is the best tool out there.
0

@don v nielsen

According to MS SQL documentation https://docs.microsoft.com/en-us/sql/t-sql/statements/create-type-transact-sql if we define UDT and set it as NOT NULL it cannot contain NULL values.
But, according to CREATE TABLE definition https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql :

An alias type based on a SQL Server system data type. Alias data types are created with the CREATE TYPE statement before they can be used in a table definition. The NULL or NOT NULL assignment for an alias data type can be overridden during the CREATE TABLE statement. However, the length specification cannot be changed; the length for an alias data type cannot be specified in a CREATE TABLE statement.


So, everything works as intended. It looks like the idea was to guarantee NOT NULL value for a column with the mentioned type but to implement it one needs define NOT NULL for each column explicitly.

0

type can be overridden during the CREATE TABLE statement "

 
So, are we in agreement that DataGrip is missing something in examining the schema and creating the DDL? 
 
We have an example of a column definition having a UDT and an override that is the opposite of what the UDT specified, and the DataGrip DLL follows the UDT and not the override. SSMS is spitting out the DDL showing the override.
 
dvn
 
p.s. I realize my example is absolutely stupid. But others far smarter than I chose to do it that way, and there is nothing I can do about it. Their implementation does follow the rules as specified.
0

> So, are we in agreement that DataGrip is missing something in examining the schema and creating the DDL? 

I can't understand what we're loosing.
If you do not explicitly set NOT NULL in table creation MS SQL treats it like NULL (since it's default) and overrides NOT NULL in UDT.

0

What I am trying to say is:

  • developer creates table with column "xxx date_nonulls null"
  • (udt date_nonulls translates to smalldatetime not null)
  • the trailing null overrides the udt, permitting nulls
  • when I recreate ddl using SSMS, SSMS generates "xxx [dbo].[Date_NONULLS] NULL,"
  • (The trailing NULL is preserved. Using the DDL will override the udt, permitting nulls)
  • when I recreate ddl using DG, it has "StopDateFrom Date_NONULLS"
  • (The trailing NULL is lost. Using the DG DDL will not override the udt; nulls are prohibitted.)
  • The end result is that DG is creating a DDL with a constraint that is the opposite of the table from which the DDL was generated.

 

 

0

Hello, Don. Thank you for clarification with nulls.

I've filed the related issue: https://youtrack.jetbrains.com/issue/DBE-6033 — you're welcome to watch/vote for.

If you know a way how to make it better than specifying nulls in all cases please comment the issue.

 

0

@ don v nielsen 
Also, pay attention to official documentation about nullability rules in table definition and database option is_ansi_nulls_on in sys.databases
https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql#nullability-rules-within-a-table-definition

0

Please sign in to leave a comment.