SQL Truncation Issue Converting VARCHAR to VARBINARY - sql

I have a fairly simple insert from a csv file into a temp table into a table with an encrypted column.
CREATE TABLE table1
(number varchar(32) NOT NULL
, user_varchar1 varchar(65) NOT NULL
, account varchar(32) NOT NULL)
CREATE TABLE #temp1
(number varchar(32) NOT NULL
, user_varchar1 varchar(65) NOT NULL
, account varchar(32) NOT NULL)
OPEN SYMMETRIC KEY SKey
DECRYPTION BY CERTIFICATE CERTCERT
--Flat File Insert
BULK INSERT #temp1
FROM '\\Server\Data\filename.csv'
WITH (FIELDTERMINATOR = ','
, FIRSTROW =2
, ROWTERMINATOR = '\n'
);
INSERT INTO table1
(number, user_varchar1, account_encrypted)
SELECT user_varchar1, number
, ENCRYPTBYKEY(KEY_GUID('SKey'),(CONVERT(varbinary(MAX), account)))
FROM #temp1
--SELECT * FROM #esa_import_ach
DROP TABLE #temp1
SELECT * FROM table1
CLOSE MASTER KEY
CLOSE SYMMETRIC KEY SKey;
The error I receive is
Msg 8152, Level 16, State 11, Line 40
String or binary data would be truncated.
Now if I allow NULLS into table1, it fills with NULLS, obviously. If I omit the account_encrypted column altogether, the script works.
If I use
INSERT INTO table1 (number, user_varchar1, account)
VALUES ('175395', '87450018RS', ENCRYPTBYKEY(KEY_GUID('SKey'), (CONVERT(varbinary(MAX), account)))
there's no problem.
So, is there something wrong with the way I'm executing the BULK INSERT, is it my declaration of the data types or is it the source file itself.
The source file looks like this (just one row):
emp_id, number, account
175395, 87450018RS,GRDI27562**CRLF**
Thanks and I'm hoping this makes sense.

The problem is that your account column is defined as varchar(32).
ENCRYPTBYKEY returns a result with a max size of 8000. That just won't fit in your column. Either expand the column, or cast the result to a smaller size to fit it inside the column. Right now it just won't fit.

Related

Inserting missing date into non nullable Datetime field

Here we have an existing database and I'm building a new system with a new database.
There I need to transfer some data from the old database table to the new database table.
I wrote this query in the SQL
INSERT INTO [Mondo-UAT].[dbo].[Countries] (
[Country_Name]
,[Country_Code]
,[Note]
)
SELECT [Code1]
,[Code3]
,[Name]
FROM [MondoErp-UAT].[dbo].[Nations]
The issue is in the [Mondo-UAT].[dbo].[Countries] table has other columns like Note is a string and Status is a bool CreateBy is int CreateDate is DateTime . So when I run the query it returns with an error
Msg 515, Level 16, State 2, Line 8 Cannot insert the value NULL into column 'CreatedDate', table 'Mondo-UAT.dbo.Countries'; column does not allow nulls. INSERT fails. The statement has been terminated
So I wanna know how to insert data for the CreateDate,CreateBy ,Notes from the above script I wrote.
If the target table has non-nullable columns without defaults, you have to specify values for those fields when inserting data.
The easiest solution would be to modify the target table to add DEFAULT values for those fields, eg SYSDATETIME() for Created, SYSTEM_USER for CreatedBy and '' for Notes. For Status you'll have to decide what a good default status is. 0 may or may not be meaningful.
Otherwise, these values will have to be specified in the query:
INSERT INTO [Mondo-UAT].[dbo].[Countries] (
[Country_Name]
,[Country_Code]
,[Note]
, Created
, CreatedBy
, Status
)
SELECT [Code1]
,[Code3]
,[Name]
, SYSDATETIME()
, SYSTEM_USER
, 0
FROM [MondoErp-UAT].[dbo].[Nations]
SYSTEM_USER returns the login name of the current user, whether it's a Windows or SQL Server login. This makes it a good default for user columns.
SYSDATETIME returns the current time as a DATETIME2(7) value. If Created is a DATETIMEOFFSET, SYSDATETIMEOFFSET should be used.
You can set Default Value for CreateDate,CreateBy,Notes columns in the [Mondo-UAT].[dbo].[Countries] table if they are not null columns. So, When you insert data into the table and do not insert a value for these columns, the default value will be inserted.

Remove quotation chars from file while bulk inserting data to table

This is Users table
CREATE TABLE Users
(
Id INT PRIMARY KEY IDENTITY(0,1),
Name NVARCHAR(20) NOT NULL,
Surname NVARCHAR(25) NOT NULL,
Email NVARCHAR(30),
Facebook NVARCHAR(30),
CHECK(Email IS NOT NULL OR Facebook IS NOT NULL)
);
This is BULK INSERT
BULK INSERT Users
FROM 'C:\Users\SAMIR\Downloads\Telegram Desktop\users.txt'
WITH (
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n',
--FIRSTROW = 0,
--UTF-8
CODEPAGE = '65001'
);
So this is Users.txt file data:
`1, N'Alex', N'Mituchin', N'qwe#gmail.com', NULL`
When I load data from the file it sets Username to values like N'Alex'. But I want to have the data simply like Alex. How can I fix this problem?
I recommend loading data into a staging table where are values are strings.
Then you can use a simply query to get the final results. In this case, you can do:
select (case when name like 'N''%'''
then substring(name, 2, len(name) - 3)
else name
end) as name
from staging
There's a better option for this. If the string delimiters and unicode indicators are consistent (they're present on all rows), you should use a format file where you can indicate delimites for each column. This will allow you to set , N' as delimiter between the first and second columns, ', N' as delimiter for the second and third columns, and so on.

Select row just inserted without using IDENTITY column in SQL Server 2012

I have a bigint PK column which is NOT an identity column, because I create the number in a function using different numbers. Anyway, I am trying to save this bigint number in a parameter #InvID, then use this parameter later in the procedure.
ScopeIdentity() is not working for me, it saved Null to #InvID, I think because the column is not an identity column. Is there anyway to select the record that was just inserted by the procedure without adding an extra ID column to the table?
It would save me a lot of effort and work if there is a direct way to select this record and not adding an id column.
insert into Lab_Invoice(iID, iDate, iTotal, iIsPaid, iSource, iCreator, iShiftID, iBalanceAfter, iFileNo, iType)
values (dbo.Get_RI_ID('True'), GETDATE(),
(select FilePrice from LabSettings), 'False', #source, #user, #shiftID, #b, #fid, 'Open File Invoice');
set #invID = CAST(scope_identity() AS bigint);
P.S. dbo.Get_RI_ID('True') a function returns a bigint.
Why don't you use?
set #invId=dbo.Get_RI_ID('True');
insert into Lab_Invoice(iID,iDate,iTotal,iIsPaid,iSource,iCreator,iShiftID,iBalanceAfter,iFileNo,iType)
values(#invId,GETDATE(),(select FilePrice from LabSettings),'False',#source,#user,#shiftID,#b,#fid,'Open File Invoice');
You already know that big id value. Get it before your insert statement then use it later.
one way to get inserted statement value..it is not clear which value you are trying to get,so created some example with dummy data
create table #test
(
id int
)
declare #id table
(
id int
)
insert into #test
output inserted.id into #id
select 1
select #invID=id from #id

Adding max(value)+1 in new row, can this be a problem?

In a SQL Server table I have the following 2 columns:
RowId: primary key, numaric, identity column and auto insert.
MailId: Non key, numaric, non identity and non auto insert.
Mail Id can be duplicate. In case of new MailId I will check max(MailId)+1 and insert it in new row and in case of duplication value will be coming as parameter.
Logic looks fine but here is an issue, I was just considering (yet chacnes of accurance are ver low) In the same time there can be two different new MailId requests. Can this casue logical error ? For example when code checked max(MailId)+1 was 101 and I stored it in a variable but may be before next insert statment executs a new record inserted in table. Now max(MailId)+1 in table will be 102 but value in variable will be 101 ?
Any suggestion please I want to control this error chances as well.
EDIT
(I am not using identity(1,1) because I also have to pass custom values in it)
Why would you use a custom-rolled Identity field when there is such a great one already in SQL Server?
Just use INT Identity (1,1) for your ID field and it will automatically increment each time a row is inserted. It also handles concurrency much better than pretty much anything you could implement manually.
EDIT:
Sample of a manual ID value:
SET IDENTITY_INSERT MyTable ON
INSERT INTO MyTable (IdField, Col1, Col2, Col3,...)
VALUES
(1234, 'Col1', 'Col2', 'Col3',...)
SET IDENTITY_INSERT MyTable OFF
You need to include an explicit field list for the INSERT.
Use OUTPUT on your insert to be sure that you have the right value. If you insert and then select MAX, it is possible that someone could "sneak" in and end up with duplication. That is, you insert MAX + 1, at the same time someone else inserts MAX + 1 then you select MAX and they select MAX, you both have the same value. Whereas if you INSERT and use OUTPUT, you'll be sure that you're unique. This is rarely a problem, but if you have a lot of activity, it can happen (speaking from experience).
EDIT
USE AdventureWorks2008R2;
GO
DECLARE #MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25,
ModifiedDate = GETDATE()
OUTPUT inserted.BusinessEntityID,
deleted.VacationHours,
inserted.VacationHours,
inserted.ModifiedDate
INTO #MyTableVar;
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate
FROM #MyTableVar;
GO
--Display the result set of the table.
SELECT TOP (10) BusinessEntityID, VacationHours, ModifiedDate
FROM HumanResources.Employee;
GO

SQL Server Concatenate string column value to 5 char long

Scenario:
I have a table1(col1 char(5)); A value in table1 may '001' or '01' or '1'.
Requirement:
Whatever value in col1, I need to retrive it in 5 char length concatenate with leading '0' to make it 5 char long.
Technique I applied:
select right(('00000' + col1),5) from table1;
I didn't see any reason, why it doesn't work? but it didn't.
Can anyone help me, how I can achieve the desired result?
Since you're using a fixed width column, it's already of size 5 (with whitespace). You need to trim it:
DECLARE #table1 TABLE (col1 char(5))
INSERT INTO #table1 (col1) VALUES ('12345')
INSERT INTO #table1 (col1) VALUES ('1')
SELECT RIGHT('00000'+RTRIM(col1),5) FROM #table1
-- Output:
-- 12345
-- 00001
Or use varchar instead:
DECLARE #table2 TABLE (col1 varchar(5))
INSERT INTO #table2 (col1) VALUES ('12345')
INSERT INTO #table2 (col1) VALUES ('1')
SELECT RIGHT('00000'+col1,5) FROM #table2
-- Output:
-- 12345
-- 00001
If you are storing the data in a CHAR field you are probably getting right spaces buffered with blanks. e.g. 01 = "01 ". If your do a RIGHT("00000" + value, 5) it'll still be the original value. You need to do a RTRIM() on the value or store the data in a VARCHAR field.
The problem is that the char(5) field is always 5 characters long, not matter what you put into it. If you insert '01' into the field, the value stored is actually '01 ' (note the trailing spaces).
Try this:
select right(('00000' + replace(col1, ' ', '')), 5)
Edit: I will leave my answer here as an example, but Michael's answer using rtrim is better.
you need to store your data in a consistent manner, so you don't need to write queries to format the data each time. this will fix your existing data:
UPDATE table1
SET col1= RIGHT('00000'+ISNULL(RTRIM(col1),''),5)
now every time you select you only have to do this:
SELECT col1 FROM table1
however, you must make sure that the data is formatted properly (leading zeros) every time it is inserted. I'd add a check constraint just to make sure:
ALTER TABLE table1 ADD CONSTRAINT
CK_table1_col1 CHECK (LEN(col1)=5)
and when you insert do this:
INSERT INTO table1
(col1, ...
VALUES
(RIGHT('00000'+ISNULL(RTRIM(#col1),''),5)