nvarchar or varchar or date in joins of SQL Server - sql

I have a management project developed in Web using SQL Server 2008 R2 and Windows Application using SQL Server CE 4.0.
Both uses almost same database structure. I have a fees table as :
TABLE [fees]
(
[feeid] bigint NOT NULL IDENTITY(1,1),
[acno] int NULL DEFAULT 0,
[billingmonth] nvarchar(10) NULL,
[oldbal] numeric(18,2) NULL DEFAULT 0,
[duedtamt] numeric(18,2) NULL DEFAULT 0,
[afterdtamt] numeric(18,2) NULL DEFAULT 0,
[bal] numeric(18,2) NULL DEFAULT 0,
[depamt] numeric(18,2) NULL DEFAULT 0,
[totdpo] numeric(18,2) NULL DEFAULT 0,
[depdt] datetime NULL
)
billingmonth will always use format MMM-yyyy eg. Jan-2018
BillingMonth uses various joins (inner and left outer join) in other tables.
How to increase performance of joins with BillingMonth ? , should i :
Convert nvarchar to varchar ( as it will always store Month in SQL2008R2 )
Convert nvarchar to datetime ( as first day of month 01-MMM-yyyy in SQL Server CE and SQL Server 2008 R2)

Storing date values as nvarchar is not recommended at all
There are many suggestions to increase the join performance:
Use Date datatype
Use two numeric field month and year instead of one varchar field (tinyint for month, smallint for year, they can be used only for joining purpose)
Note that: AS #Pரதீப் mentioned, When you store month and year separately, you need to do some integer manipulations when searching for date ranges

Related

Binary(16) truncate to binary(4) in CASE statement

Have a table:
CREATE TABLE [db].[Table1](
[Id] [int] NOT NULL,
[Hash] [binary](16) NOT NULL
)
With data:
Id Hash
1 0x00000000000000000000000000000000
2 0x00000000000000000000000000000000
And trying to execute SQL:
UPDATE Table1 SET Hash = CASE Id
WHEN 1 THEN 0x4cb47abddf8a9c348c7a7c20abd0b1d5
ELSE 0
END
Expect that value in column Hash is 0x4cb47abddf8a9c348c7a7c20abd0b1d5 but actual is 0x00000000000000000000000abd0b1d5.
If I try:
UPDATE Table1 SET Hash = 0x4cb47abddf8a9c348c7a7c20abd0b1d5
Everything is ok.
Binary has the lowest precedence of any datatype in SQL Server (Data Type Precendence (SQL Server)):
SQL Server uses the following precedence order for data types:
user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)
With a CASE expression, all return values are implicitly converted to the datatype with the highest value in the return values. In this case you have the int value 0, and the binary(16) value 0x4cb47abddf8a9c348c7a7c20abd0b1d5. As int > binary, the value 0x4cb47abddf8a9c348c7a7c20abd0b1d5 is converted to an int, and thus you loss data.
If the return types of your data is important, and you are using multiple data types in your return values, explicitly convert all the values to the correct type. In this case:
UPDATE Table1
SET Hash = CASE Id WHEN 1 THEN 0x4cb47abddf8a9c348c7a7c20abd0b1d5
ELSE CONVERT(binary(16),0)
END;

Error message when inserting date into table

when I try to insert my date values in all my other tables it works fine, except of one. Whatever format I try I always get the error, that the inserted value couldn't be converted from an input char to the expected date format.
That's how I insert my values
-- ServiceTicket
INSERT INTO ServiceTicket
VALUES ('90000', '01-5-2019', '50000', '10000', '70000', 200.00, 100.00, 5.00, 350.00) --Error converting into DATE type
And this is the table structure:
CREATE TABLE dbo.ServiceTicket (
ticketIssueNo INT NOT NULL IDENTITY(1,1) PRIMARY KEY, --Identity autoincrements
serviceDate DATE NOT NULL,
vehicleId CHAR(8) NOT NULL,
customerId CHAR(8) NOT NULL,
inspectionId CHAR(8) NOT NULL,
serviceCost DECIMAL(10,4) NOT NULL CHECK(serviceCost BETWEEN 0.0 AND 99999.0) DEFAULT 0.0,
inspectionCost DECIMAL(10,4) NOT NULL CHECK(inspectionCost BETWEEN 0.0 AND 99999.0) DEFAULT 0.0,
repairCost DECIMAL(2,2) NOT NULL CHECK(repairCost BETWEEN 0.0 AND 99999.0) DEFAULT 0.0,
GST DECIMAL(10,4) NOT NULL DEFAULT 0.0,
amountDue DECIMAL(10,4) NOT NULL CHECK(amountDue BETWEEN 0.0 AND 99999.0) DEFAULT 0.0,
FOREIGN KEY(vehicleId) REFERENCES Vehicle(vehicleId)
ON UPDATE NO ACTION,
FOREIGN KEY(inspectionId) REFERENCES VehicleInspection(inspectionId)
ON UPDATE NO ACTION,
FOREIGN KEY(customerId) REFERENCES Customer(customerId)
ON UPDATE NO ACTION
)
GO
I might overlook something.
Always list the columns when writing an insert statement.
Here's an exact equivalnet of your insert statement, written properly, based on the DDL you've published:
INSERT INTO ServiceTicket
(serviceDate, vehicleId , customerId, inspectionId , serviceCost , inspectionCost, repairCost, GST , amountDue) VALUES
('90000' , '01-5-2019' , '50000' , '10000' , '70000' , 200.00 , 100.00 , 5.00 , 350.00)
I've used tabs so that each value would be perfectly aligned with the column it goes into, that helps a lot when you have a long list of columns.
As you can clearly see, the serviceDate gets the value '90000' - while it shoud clearly be '01-5-2019'
Always use ISO8601 format for string representation of date / datetime values.
Any other format is culture dependent, and the worst thing about it is that it depends on the default language of the login - so different logins might have different results if you use a culture-dependent format. The ISO8601 standard provides two alternatives for datetime formats: yyyy-mm-ddThh:mm:ss or yyyymmddThhmmss. If you are inserting only a date only string value into a DateTime data type column / variable, be sure to use only the second (yyyymmdd) format - because yyyy-mm-dd is still culture dependent with DateTime (but not with Date or with DateTime2 - that's one more reason why you should never use DateTime again.
So the proper way of writing the insert statement would be this:
INSERT INTO ServiceTicket
(serviceDate, vehicleId , customerId, inspectionId , serviceCost , inspectionCost, repairCost, GST , amountDue) VALUES
('2019-05-01', '90000' , '50000' , '10000' , '70000' , 200.00 , 100.00 , 5.00 , 350.00)
(That is, assuming 01-5-2019 stands for May 1st. If it stands for January 5th, it should be 2019-01-05).
The date format '01-5-2019' which you are using does not appear to be a default accepted date literal by SQL Server. Try using '20190501':
INSERT INTO ServiceTicket (serviceDate, ... <other columns>)
VALUES
('20190501', ...);
Note that YYYYMMDD is an unambiguous unseparated ISO format, as the documentation discusses.
The format for inserting date type in SQL is YYYY-dd-MM, You need to change it to '2019-05-01'
Change your date format from '01-5-2019' to '2019-05-01'.
Instead passing date as '01-5-2019' pass CAST('01-5-2019' AS DATE).
The default format for the year data type in SQL is YYYY-MM-DD(If you've not changed it before) Info. So as described here, you should convert the input string into a valid date type.

Datetime column cannot be pesisted

I have this SQL statement to create a table that stores the JSON string data and the event time found in that JSON string.
CREATE TABLE [dbo].[EventLog]
(
[EventID] INT NOT NULL IDENTITY(1,1),
[EventTime] AS CAST(JSON_VALUE(RawEvent, '$.EventTime') AS DATETIME ) PERSISTED,
[RawEvent] NVARCHAR(MAX) NOT NULL
)
However I get the following error below when I run this, I assume SQL Server does not know if the value fits DATETIME? is there a way to get this column defined?
Msg 4936, Level 16, State 1, Line 26
Computed column 'EventTime' in table 'Event' cannot be persisted because the column is non-deterministic.
You can use CONVERT with dates and have deterministic behavior as long as you specify certain date styles. As per the docs here, with the most common JavaScript date formats (since you are converting from JSON), you can use style 126 or 127, which are ISO8601 and ISO8601 with time zone. Your table, then, could be specified like this:
CREATE TABLE [dbo].[EventLog]
(
[EventID] INT NOT NULL IDENTITY(1,1),
[EventTime] AS CONVERT(DATETIME, JSON_VALUE(RawEvent, '$.EventTime'), 126) PERSISTED,
[RawEvent] NVARCHAR(MAX) NOT NULL
)
Alas, this is explained in the documentation:
CAST Deterministic unless used with datetime, smalldatetime, or sql_variant.
You may be able to parse the date and reconstruct the value using datefromparts() or datetimefromparts().

datatype for an iif statement

If in a SELECT statement there is the following line:
iif(fr.BoundID=0,'OutBound','InBound') as 'FlightBound'
Then when I perform a CREATE TABLE statement, should I include the actual datatype of the BoundID field which is a tinyint in the database table, or shall the datatype be varchar because I think (but not 100% sure looking at the existing code) that the previous person writing this code is saying display 'OutBound, InBound' if the ID is 0?
In your case it will be VARCHAR(8). You can always check metadata using sys.dm_exec_describe_first_result_set:
SELECT *
FROM sys.dm_exec_describe_first_result_set(
'SELECT iif(BoundID=0,''OutBound'',''InBound'') as ''FlightBound''
FROM #tab',NULL,0)
LiveDemo
What datatype to choose for new table TINYINT vs Textual representation is dependent on your business requirements. I would probably stay with TINYINT (search for lookup table named like BoundType or ask senior developer/architect).
The return type is the type in true_value and false_value with the highest precendence (reference, see return types)
Returns the data type with the highest precedence from the types in true_value and false_value. For more information, see Data Type Precedence (Transact-SQL).
Data type precendence here up to SQL Server 2016:
user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)

SQLite compare dates

I have this SQL-Statement:
SELECT Geburtsdatum FROM Kunde
WHERE Geburtsdatum BETWEEN '1993-01-01' AND '2000-01-01'
but I get some weird results, like: 2.02.1990
'Geburtsdatum' is a DATE
Any suggestions or solutions?
my table-structure:
CREATE TABLE Kunde (
Kunde_ID INTEGER NOT NULL ,
Card INTEGER ,
Vorname VARCHAR(255) NOT NULL ,
Nachname VARCHAR(255) NOT NULL ,
Ort VARCHAR(255) NOT NULL ,
Strasse VARCHAR(255) NOT NULL ,
Postleitzahl VARCHAR(10) NOT NULL ,
Mail VARCHAR(255) ,
Telefonnummer VARCHAR(255) ,
Geburtsdatum DATE NOT NULL ,
Beitrittsdatum DATE NOT NULL ,
Geschlecht INTEGER NOT NULL ,
Land VARCHAR(255) NOT NULL DEFAULT 'Österreich' ,
Bankname VARCHAR(255) ,
Bankleitzahl VARCHAR(255) ,
Kontonummer VARCHAR(255) ,
GroupID INTEGER NOT NULL ,
Besucher INTEGER ,
Access BOOLEAN ,
image BLOB NULL ,
writeDate DATE ,
drinkAbo BOOLEAN ,
PRIMARY KEY (Kunde_ID) )
From the documentation:
SQLite does not have a storage class set aside for storing dates and/or times.
So your column isn't exactly stored as a date. Reading further, we learn that columns specifed as DATE are actually stored as NUMERIC using affinity rule 5.
Going back up to section 1.2:
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
Good. So let's try:
SELECT Geburtsdatum FROM Kunde
WHERE Geburtsdatum
BETWEEN julianday('1993-01-01') AND julianday('2000-01-01');
Oddly enough, SQL Fiddle seems to store DATEs as strings and the above doesn't work. In this case, the following should:
SELECT Geburtsdatum FROM Kunde
WHERE date(Geburtsdatum)
BETWEEN date('1993-01-01') AND date('2000-01-01');
Additionally, in your case you seem to be getting some strange (read: localized) format returned. I wonder if it really is a string in your case too, just with a different format. You could try:
SELECT Geburtsdatum FROM Kunde
WHERE strftime('%d.%m.%Y', Geburtsdatum)
BETWEEN date('1993-01-01') AND date('2000-01-01');
Someone had the same problem and got it resolved. (use datetime function before comparison)
See SQLite DateTime comparison
Excerpt:
Following the datetime function and having a string format as YYYY-MM-DD HH:mm:ss i achieved good results as follows
select *
from table_1
where mydate >= Datetime('2009-11-13 00:00:00')
and mydate <= Datetime('2009-11-15 00:00:00')
--EDIT--
You are basically comparing strings. Which is why the unexpected behavior. Convert to datetime using the function right before comparison and it should work.
To be able to compare dates, you must store them in one of SQLite's supported date formats, such as a JJJJ-MM-TT string. Your dates are stored as strings in a localized date format which is not recognized by SQLite.
If the most significant field is not at the start of the string, string comparisons will not work for dates.
With the values currently in your table, you will not be able to do any comparisons.