SQL Server Always Encrypted - Maximum number of parameters - sql

I have a SQL Server 2016 database with "Always Encrypted" set to on. We have multiple encrypted columns and a stored procedure that handles updates.
CREATE PROCEDURE UpdateRecord
#ID INT,
#EncryptedParameter01 NVARCHAR(255) = NULL,
#EncryptedParameter02 NVARCHAR(255) = NULL,
#EncryptedParameter03 NVARCHAR(255) = NULL,
#EncryptedParameter04 NVARCHAR(255) = NULL,
#EncryptedParameter05 NVARCHAR(255) = NULL,
#EncryptedParameter06 NVARCHAR(255) = NULL,
#EncryptedParameter07 NVARCHAR(255) = NULL,
#EncryptedParameter08 NVARCHAR(255) = NULL,
#EncryptedParameter09 NVARCHAR(255) = NULL,
#EncryptedParameter10 NVARCHAR(255) = NULL,
#EncryptedParameter11 NVARCHAR(255) = NULL,
#EncryptedParameter12 NVARCHAR(255) = NULL,
#EncryptedParameter13 NVARCHAR(255) = NULL,
#EncryptedParameter14 NVARCHAR(255) = NULL,
#EncryptedParameter15 NVARCHAR(255) = NULL,
#EncryptedParameter16 NVARCHAR(255) = NULL,
#EncryptedParameter17 NVARCHAR(255) = NULL,
#EncryptedParameter18 NVARCHAR(255) = NULL,
#EncryptedParameter19 NVARCHAR(255) = NULL,
#EncryptedParameter20 NVARCHAR(255) = NULL
AS
BEGIN
UPDATE myTable
SET Field01 = CASE WHEN #EncryptedParameter01 IS NULL THEN Field01 ELSE #EncryptedParameter01 END,
Field02 = CASE WHEN #EncryptedParameter02 IS NULL THEN Field02 ELSE #EncryptedParameter02 END,
Field03 = CASE WHEN #EncryptedParameter03 IS NULL THEN Field03 ELSE #EncryptedParameter03 END,
Field04 = CASE WHEN #EncryptedParameter04 IS NULL THEN Field04 ELSE #EncryptedParameter04 END,
Field05 = CASE WHEN #EncryptedParameter05 IS NULL THEN Field05 ELSE #EncryptedParameter05 END,
Field06 = CASE WHEN #EncryptedParameter06 IS NULL THEN Field06 ELSE #EncryptedParameter06 END,
Field07 = CASE WHEN #EncryptedParameter07 IS NULL THEN Field07 ELSE #EncryptedParameter07 END,
Field08 = CASE WHEN #EncryptedParameter08 IS NULL THEN Field08 ELSE #EncryptedParameter08 END
, Field09 = CASE WHEN #EncryptedParameter09 IS NULL THEN Field09 ELSE #EncryptedParameter09 END
, Field10 = CASE WHEN #EncryptedParameter10 IS NULL THEN Field10 ELSE #EncryptedParameter10 END
, Field11 = CASE WHEN #EncryptedParameter11 IS NULL THEN Field11 ELSE #EncryptedParameter11 END
, Field12 = CASE WHEN #EncryptedParameter12 IS NULL THEN Field12 ELSE #EncryptedParameter12 END
, Field13 = CASE WHEN #EncryptedParameter13 IS NULL THEN Field13 ELSE #EncryptedParameter13 END
, Field14 = CASE WHEN #EncryptedParameter14 IS NULL THEN Field14 ELSE #EncryptedParameter14 END
, Field15 = CASE WHEN #EncryptedParameter15 IS NULL THEN Field15 ELSE #EncryptedParameter15 END
, Field16 = CASE WHEN #EncryptedParameter16 IS NULL THEN Field16 ELSE #EncryptedParameter16 END
, Field17 = CASE WHEN #EncryptedParameter17 IS NULL THEN Field17 ELSE #EncryptedParameter17 END
, Field18 = CASE WHEN #EncryptedParameter18 IS NULL THEN Field18 ELSE #EncryptedParameter18 END
, Field19 = CASE WHEN #EncryptedParameter19 IS NULL THEN Field19 ELSE #EncryptedParameter19 END
, Field20 = CASE WHEN #EncryptedParameter20 IS NULL THEN Field20 ELSE #EncryptedParameter20 END
WHERE ID = #ID
END
Our actual stored procedure is more complex, this just gives you an idea. If this is called with only a handful of parameters it works fine. However if it is called with more than 10 the procedure fails.
The error message is
Msg 206, Level 16, State 2, Procedure UpdateRecord, Line 0 [Batch Start Line 0]
Operand type clash: nvarchar is incompatible with nvarchar(255) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'MyNamedKey', column_encryption_key_database_name = 'MyDatabase')
This is not down to individual parameters failing, rather the number of characters being passed. Also this procedure was working fine before the encryption was added.
Any further info on why this is happening and work arounds gratefully received.
Many thanks.
Edited to include call:
I am seeing this both through VB and T-SQL. Simplest case being a direct call from SSMS. I do have "Enable Parameterization for Always Encrypted" checked. The parameters are being passed in correctly, and functions correctly, provided 10 or less parameters are sent. Example call:
DECLARE #PassedEncryptedParameter01 NVARCHAR(255) = 'Updated value 01'
EXEC UpdateRecord #Id=12345, #EncryptedParameter01 = #PassedEncryptedParameter01
As you can see I have the declared the type so it matches. The issue here isn't how the parameters are defined, rather the number of parameters being sent.

Related

Where clause filter in stored procedure

I'm trying to create a procedure where it needs to filter the records with where condition by multiple scalar parameters. I'm facing the problem with the column I.[status].
In that column the values will be as "pending", "rejected", "submitted". But when the value comes with "All", I need to select all the status types (pending, rejected, submitted)
CREATE PROCEDURE [dbo].[sp_cc_get_Invoices]
(#po_id INT = NULL,
#sortBy VARCHAR(50) = NULL,
#sortDirection VARCHAR(50) = 0,
#pageSize INT = NULL,
#page INT = NULL,
#TotalRows INT = NULL OUTPUT,
#time_zone_offset INT = 0,
#vendor_id VARCHAR(MAX) = NULL,
#status VARCHAR(20) = 'All',
#invoice_id INT = NULL,
#invoice_from_dt DATETIME2 = NULL,
#invoice_to_dt DATETIME2 = NULL)
AS
BEGIN
SELECT
I.invoice_id, I.invoice_amount, I.[status],
v.vendor_id, I.po_id, v.vendor_name,
I.netsuit_invoice_id, cd.currency_symbol
FROM
invoice_details I
LEFT JOIN
vendor_details v ON v.vendor_id = I.vendor_id
LEFT JOIN
currency_details cd ON cd.currency_id = I.currency_id
WHERE
(#po_id IS NULL OR I.po_id = #po_id)
AND (#vendor_id IS NULL OR I.vendor_id = #vendor_id)
AND (#invoice_id IS NULL OR #invoice_id = '' OR I.invoice_id = #invoice_id)
AND (I.invoice_date BETWEEN #invoice_from_dt AND #invoice_to_dt)
AND I.[status] = #status
END
How to filter all the status records?
You can express this with boolean logic.
Just change:
and I.[status] = #status
To:
and (#status = 'All' or I.[status] = #status)

Using COALESCE in Sql server

I have a stored procedure which will update the album details if a album id is present in the table , if not it will add a new record.
User some times can update all values, or some times leaves some values. So in case if the user leave the values for updating old data should be kept in table.
For this i found solution to make use of coasesce in sql...
I wrote my SP as:
UPDATE [dbo].[tbl_M_Album]
SET [AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = coalesce(#Description,[Description])
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
where AlbumID =#AlbumId
end
If i did not send the Description the old data is not keeping, it is overriding by empty data.
Please some one help me if i have any mistakes..
The value of #Description you pass is blank or empty, not NULL.
COALESCE and other null checking functions treat '' as a non null value.
These are all true,
NULL IS NULL
'' IS NOT NULL
' ' IS NOT NULL
If you want to check for NULL, '' (empty) or ' ' (whitespace), you could use
COALESCE(LEN(TRIM(#Description)), 0) = 0
or, for just NULL and empty,
COALESCE(LEN(#Description), 0) = 0
but, it would be more efficient to avoid passing empty or blank values.
You could rewrite your SP like this
IF COALESCE(LEN(TRIM(#Description)), 0) = 0
UPDATE [dbo].[tbl_M_Album]
SET
[AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
WHERE
[AlbumID] = #AlbumId;
ELSE
UPDATE [dbo].[tbl_M_Album]
SET
[AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = #Description
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
WHERE
[AlbumID] = #AlbumID;
Check if the value assigned for #Description is an empty string or NULL. If you pass an empty string in #Description then that is the problem. Only passing NULL will "default" to the next non-null value (this applies to both COALESCE or ISNULL).
Hope this helps.
Use this query.
UPDATE [dbo].[tbl_M_Album]
SET [AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = Case WHEN (#Description IS NULL OR LTRIM(RTRIM(#Description) = '')
THEN [Description]
ELSE #Description
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
where AlbumID =#AlbumId
end

Using case to delete data row

I created a simple stored procedure to update my table, but currently what I need is
Delete * from [ABCSystem].[dbo].[NEW_TEST_NUMBER]
WHERE sONbr = #sONbr AND SOLine = #SOLine
If #Statuscode is = "N001" by using case. Anyone can show me a simple sample or something?
Thank you.
ALTER PROCEDURE [dbo].[usp_Testing]
-- Add the parameters for the stored procedure here
#sONbr nvarchar(50) = NULL,
#SOLine nvarchar(50) = NULL,
#SerialNbr nvarchar(50) = NULL,
#StatusCode nvarchar(50) = NULL,
#PackType nvarchar(50) = NULL,
#PalletID nvarchar(50) = NULL,
#PackingListNo nvarchar(50) = NULL,
#CrDateTime nvarchar(50) = NULL,
#CrUserID nvarchar(50) = NULL,
#return nvarchar(50) = NULL OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF EXISTS(
SELECT sONbr , SOLine
FROM [ABCSystem].[dbo].[NEW_TEST_NUMBER]
WHERE sONbr = #sONbr AND SOLine = #SOLine
)
BEGIN
UPDATE [ABCSystem].[dbo].[NEW_TEST_NUMBER]
SET StatusCode = #StatusCode
,LastUpdDateTime = GETDATE()
,LastUpdUserID = #CrUserID
,StatusDesc =
CASE #StatusCode WHEN 'N001' THEN 'New'
WHEN 'PR001' THEN 'Prepack In Progress'
WHEN 'PR002' THEN 'PrePacking Completed'
WHEN 'WE002' THEN 'Weight Complete'
END
,PalletID =
CASE #StatusCode WHEN 'N001' THEN cast(null as nvarchar(50))
ELSE PalletID
END
,PackType =
CASE #StatusCode WHEN 'N001' THEN cast(null as nvarchar(50))
ELSE PackType
END
,JobID =
CASE #StatusCode WHEN 'N001' THEN cast(null as nvarchar(50))
ELSE JobID
END
,JobCrDateTime =
CASE #StatusCode WHEN 'N001' THEN cast(null as nvarchar(50))
ELSE JobCrDateTime
END
,PackingListNo =
CASE #StatusCode WHEN 'N001' THEN cast(null as nvarchar(50))
ELSE PackingListNo
END
WHERE sONbr = #sONbr AND SOLine = #SOLine
IF ##ERROR <> 0
Set #Return = 'UPDATE FAILED!'
ELSE
Set #Return = 'UPDATE SUCCESSFULLY.'
END
ELSE
BEGIN
Set #Return = 'NO DATA EXIST!'
END
As far as I am seing your problem, you have to create a simple table that will tell you INSERT, UPDATE, DELETE. Link the table inside your stored proc. This will help you to sort things down.
OR
Simply sort everything in loop inside stored proc telling that
WHEN #StatusCode = 'N001'
DO update
ELSE
DO something else

How to run stored procedure query between 2 table & insert data base on reference column?

I've created a simple stored procedure to update my Sample_TAG_NUMBERTEST table but I need it to get an ItemCode from another table PartItem by using both SOLine & SONbr to do the query but I don't know how to write it.
2nd thing I need to do is when my StatusCode column is N001 then my DESC column will automatically insert NEW ORDER data value, for PR002 my DESC column will become Progress.
How can I do that? Thanks in advance.
ALTER PROCEDURE [dbo].[SampleTagNumberUpdate]
#sONbr nvarchar(50) = NULL,
#SOLine nvarchar(50) = NULL,
#SerialNbr nvarchar(50) = NULL,
#StatusCode nvarchar(50) = NULL,
#PackType nvarchar(50) = NULL,
#PalletID nvarchar(50) = NULL,
#PackingListNo nvarchar(50) = NULL,
#ItemCode nvarchar(50) = NULL,
#CrDateTime nvarchar(50) = NULL,
#CrUserID nvarchar(50) = NULL,
#return nvarchar(50) = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT sONbr , SOLine
FROM [SampleSystem].[dbo].[Sample_TAG_NUMBERTEST]
WHERE sONbr = #sONbr AND SOLine = #SOLine)
BEGIN
UPDATE [SampleSystem].[dbo].[Sample_TAG_NUMBERTEST]
SET SerialNbr = #SerialNbr
,StatusCode = #StatusCode
,PackType = #PackType
,PalletID = #PalletID
,PackingListNo = #PackingListNo
,ItemCode = #ItemCode
,LastUpdDateTime = GETDATE()
,LastUpdUserID = #CrUserID
WHERE sONbr = #sONbr AND SOLine = #SOLine
IF ##ERROR <> 0
Set #Return = 'UPDATE FAILED'
ELSE
Set #Return = 'UPDATE SUCCESSFULLY'
END
ELSE
BEGIN
INSERT INTO [SampleSystem].[dbo].[Sample_TAG_NUMBERTEST](SONbr, SOLine, SerialNbr
,StatusCode
,PackType
,PalletID
,PackingListNo
,ItemCode
,CrDateTime
,CrUserID)
VALUES(#sONbr, #SOLine, #SerialNbr, #StatusCode, #PackType
,#PalletID
,#PackingListNo
,#ItemCode
,GETDATE()
,#CrUserID)
IF ##ERROR <> 0
Set #Return = 'INSERT DATA FAILED'
ELSE
Set #Return = 'INSERT DATA SUCCESSFULLY'
END
END
I believe I have provided an example for both questions below:
UPDATE T
SET ItemCode = (SELECT PI.ItemCode FROM PartItem AS PI WHERE PI.SO = #SO AND PI.SO_LINE = #SO_LINE),
DESC = CASE #StatusCode WHEN 'N001' THEN 'NEW ORDER'
ELSE CASE #StatusCode WHEN 'PR002' THEN 'PROGRESS' ELSE '' END
END,
<other columns here...>
FROM SAMPLE_TAG_NUMBERTEST AS T

Insert Stored Procedure with null parameters

I want to create an insert stored procedure with null parameters, if pass the value for that parameter then it has to insert or update in database
My stored procedure is:
Create proc [dbo].[SP_InsertOrUpdateCourseDetails]
#CourseID int,
#Tab1Title nvarchar(250) = null,
#Tab1Description nvarchar(max) = null,
#Tab2Title nvarchar(250) = null,
#Tab2Description nvarchar(max) = null,
#Tab3Title nvarchar(250) = null,
#Tab3Description nvarchar(max) = null,
#Tab4Title nvarchar(250) = null,
#Tab4Description nvarchar(max) = null,
#Syllabus nvarchar(max) = null
As
Begin
If NOT EXISTS (Select * from CourseDetail Where CourseID=#CourseID )
Begin
Insert into CourseDetail(CourseID, Tab1Title, Tab1Description,
Tab2Title, Tab2Description,
Tab3Title, Tab3Description, Tab4Title, Tab4Description,
Syllabus)
values (#CourseID, #Tab1Title, #Tab1Description, #Tab2Title, #Tab2Description,
#Tab3Title, #Tab3Description, #Tab4Title, #Tab4Description, #Syllabus)
IF ##ERROR = 0 AND ##ROWCOUNT =1
Begin
Select top 1 CourseID from CourseDetail Order by CourseDetailID Desc
End
Else
Begin
Select 0
End
End
Else
Begin
Update CourseDetail
SET
Tab1Title = #Tab1Title,
Tab1Description = #Tab1Description,
Tab2Title = #Tab2Title,
Tab2Description = #Tab2Description,
Tab3Title = #Tab3Title,
Tab3Description = #Tab3Description,
Tab4Title = #Tab4Title,
Tab4Description = #Tab4Description,
Syllabus = #Syllabus
Where
CourseID = #CourseID
IF ##ERROR = 0 AND ##ROWCOUNT =1
Begin
Select top 1 CourseID from CourseDetail Order by CourseDetailID Desc
End
Else
Begin
Select 0
End
End
End
You can use ISNULL function passing in it SP's parameter as first parameter and default value in case of insert or actual column's value in case of update:
Insert into CourseDetail(CourseID, Tab1Title, Tab1Description,
Tab2Title, Tab2Description,
Tab3Title, Tab3Description, Tab4Title, Tab4Description,
Syllabus)
values (#CourseID, ISNULL(#Tab1Title, 'default value'), ISNULL(#Tab1Description,'default value'), ...
Update CourseDetail
SET
Tab1Title = ISNULL(#Tab1Title, Tab1Title),
Tab1Description = ISNULL(#Tab1Description, Tab1Description),
....
Where
CourseID = #CourseID
But in this case you won't can to set NULL value explicitely even if you'll need