decryption following hash for same Id is not working - sql

I have two seperate stored procedures which one is for adding a new employee to DB and the other is for getting an employee from the DB.
I'm using SHA2_256 and it looks like it works good when inserting the data, but when using the same technique for getting the employee, something is not working.
This is the SP for adding the employee.
USE [db11]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[io_sp_admin_add_emp]
#Id BIGINT, #Lname VARCHAR(20), #Fname VARCHAR(15),#Gender TINYINT,#Bday DATETIME,#LoggedInUser VARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
DECLARE #HashId varbinary(50) = HashBytes('SHA2_256', cast(#Id as varbinary(50)))
INSERT INTO io_t_employees(
lname,
fname,
gender,
bday,
[user_name],
hash_id
)
VALUES(
LTRIM(RTRIM(#Lname)),
LTRIM(RTRIM(#Fname)),
#Gender,
#Bday,
#loggedInUser,
#HashId,
)
SELECT CAST(1 as BIT) as 'Status', 'Succeeded' as 'ReturnMessage'
END TRY
BEGIN CATCH
END
And Then, I would like to get the User's UniqueId according to the HashId stored earlier.
USE [db11]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[io_sp_admin_emp_helper]
-- Add the parameters for the stored procedure here
#id INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT UniqueId
FROM io_t_employees
WHERE hash_id = HashBytes('SHA2_256', cast(#id as varbinary(50)))
END
Unfortunately ,the result of the second procedure's query has no data.
I have a feeling I'm doing something wrong in the first procedure (add employee).

Related

How to return an id and use it directly in another stored procedure?

I want his stored procedure to return the inserted id
ALTER PROCEDURE [dbo].[InsertAddress_DBO]
#Name VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[Address]([Address_Name])
OUTPUT INSERTED.Address_Id
VALUES (#Name)
END
This one the same
ALTER PROCEDURE [dbo].[InsertDocumentation_DBO]
#Texte VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[Documentation]([Documentation_Text])
OUTPUT inserted.Documentation_Id
VALUES (#Texte)
END
And this one to use them and return her own -
like using the inserted id to put it into the next stored procedure as a parameter
ALTER PROCEDURE [dbo].[InsertEstablishmentByStrings_DBO]
#Establishment_Name VARCHAR(50),
#Address_Name VARCHAR(50),
#Documentation_Text VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Address_ID INT ,
#Documentation_ID INT
EXEC #Address_ID = [dbo].[InsertAddress_DBO]
#Name = "rue de la banchiesserie 85 Golback"
EXEC #Documentation_ID = [dbo].[InsertDocumentation_DBO]
#Texte = "né en 55555 restaurant fabuleux"
INSERT INTO [dbo].[Establishment]([Establishment_Name],[Address_Id],[Documentation_Id])
OUTPUT inserted.Establishment_Id
VALUES (#Establishment_Name,#Address_ID,#Documentation_ID)
END
However, I always get an error, because the stored procedure doesn't return the id when I execute it.
What is wrong in my code?
I would like to get the code I could use again and again in each stored procedure I have to execute. I already tried ##Identity, indent, scoped,... nothing works.
If you want to return something from stored procedure to the context of SQL query execution you may use a return statement or an output parameter. I would suggest you to use the second option. The first one is generally intended to return status of procedure execution.
ALTER PROCEDURE [dbo].[InsertAddress_DBO]
#Name VARCHAR(50),
#Address_ID INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[Address]([Address_Name])
VALUES (#Name)
SET #Address_ID = SCOPE_IDENTITY()
END
Than you can use returned value in your outer procedure
ALTER PROCEDURE [dbo].[InsertEstablishmentByStrings_DBO]
#Establishment_Name VARCHAR(50),
#Address_Name VARCHAR(50),
#Documentation_Text VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Address_ID INT ,
#Documentation_ID INT
EXEC [dbo].[InsertAddress_DBO]
#Address_ID = #Address_ID OUTPUT,
#Name = "rue de la banchiesserie 85 Golback"
...
END
An OUTPUT INSERTED clause you used doesn't returns data to the query execution context but send them to the output stream.
Your stored procedures should look like this, using an OUTPUT parameter, not trying to consume a RETURN value (which should never contain data) using a resultset. Also [don't] [put] [everything] [in] [square] [brackets] [unless] [you] [have] [to], [because] [all] [it] [does] [is] [hamper] [readability], and don't surround string literals with "double quotes" because that means something else in T-SQL.
CREATE OR ALTER PROCEDURE dbo.InsertAddress_DBO
#Name varchar(50),
#Address_Id int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Address(Address_Name)
VALUES (#Name);
SELECT #Address_Id = SCOPE_IDENTITY();
END
GO
CREATE OR ALTER PROCEDURE dbo.InsertDocumentation_DBO
#Texte varchar(50),
#Doc_Id int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Documentation(Documentation_Text)
VALUES (#Texte);
SELECT #Doc_Id = SCOPE_IDENTITY();
END
GO
Now, your main procedure can do this:
CREATE OR ALTER PROCEDURE dbo.InsertEstablishmentByStrings_DBO
#Establishment_Name varchar(50),
#Address_Name varchar(50),
#Documentation_Text varchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Address_ID INT ,
#Documentation_ID INT
EXEC dbo.InsertAddress_DBO
#Name = #Address_Name,
#Address_Id = #Address_ID OUTPUT;
EXEC dbo.InsertDocumentation_DBO
#Texte = Documentation_Text,
#Doc_Id = #Documentation_ID OUTPUT;
INSERT dbo.Establishment
(Establishment_Name, Address_Id, Documentation_Id)
OUTPUT inserted.Establishment_Id,
inserted.Address_ID, inserted.Documentation_ID
VALUES (#Establishment_Name,#Address_ID,#Documentation_ID);
END
GO
And you call it like this:
EXEC dbo.InsertEstablishmentByStrings_DBO
#Establishment_Name = 'Gaston''s',
#Address_Name = 'rue de la banchiesserie 85 Golback',
#Documentation_Text = 'né en 55555 restaurant fabuleux';
And get results like this:
Establishment_Id
Address_ID
Documentation_ID
1
1
1
Fully working example on db<>fiddle

Update followed by insert in a stored procedure

I'm not sure that's the correct way making an update followed by insert in a stored procedure.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[io_sp_admin_add_emp]
#id BIGINT,
#lastName VARCHAR(20),
#firstName VARCHAR(20)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION [TranAddEmp]
DECLARE #identity BIGINT = 0
INSERT INTO empTable(LastName, FirstName, hash_id)
VALUES (#lastName, #firstName,
HashBytes('SHA2_256', CAST(#id AS VARBINARY(50))))
SELECT #identity = ##identity
UPDATE empTable
SET rowId = incId -- both are columns in empTable
WHERE hash_id = #identity
COMMIT TRANSACTION [TranAddEmp]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [TranAddEmp]
END CATCH
END
A simple change to your current code can give you what you're looking for.
Instead of messing around with ##Identity, which is almost never the right thing to do, you compute the hash of the #Id value once, store it in a local variable, and use it for both the insert statement and the where clause of the update statement - That is, assuming the HashId column is unique.
That being said, I'm not sure why you need the rowId column as well as the incId column - unless one of them is designed to change it's value through an update statement in the lifetime of the row - you are simply keeping redundant data.
Here's an improved version of your stored procedure:
CRETAE PROCEDURE [dbo].[io_sp_admin_add_emp]
#id BIGINT,
#lastName varchar(20),
#firstName varchar(20)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION [TranAddEmp]
-- Compute the hash once, store in a local variable
DECLARE #HashId varbinary(8000) = HashBytes('SHA2_256', cast(#id as varbinary(50)))
INSERT INTO empTable(
LastName,
FirstName,
hash_id
)
VALUES(
#lastName,
#firstName,
#HashId
)
UPDATE empTable
SET rowId = incId
WHERE hash_id = #HashId
COMMIT TRANSACTION [TranAddEmp]
END TRY
BEGIN CATCH
-- make sure transaction has started and is not commited
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION [TranAddEmp]
END CATCH
END
There is a great keyword OUTPUT. As MSDN says:
Returns information from, or expressions based on, each row affected
by an INSERT, UPDATE, DELETE, or MERGE statement. These results can be
returned to the processing application for use in such things as
confirmation messages, archiving, and other such application
requirements. The results can also be inserted into a table or table
variable. Additionally, you can capture the results of an OUTPUT
clause in a nested INSERT, UPDATE, DELETE, or MERGE statement, and
insert those results into a target table or view.
You can insert your inserted id's into table through OUTPUT keyword. For example:
DECLARE #InsertedIDs TABLE (ID varbinary(8000))
INSERT INTO empTable(
LastName,
FirstName,
hash_id
)
OUTPUT HashBytes('SHA2_256', cast(INSERTED.ID as varbinary(50))) INTO #InsertedIDs(ID)
VALUES(
#lastName,
#firstName,
HashBytes('SHA2_256', cast(#id as varbinary(50)))
)
UPDATE empTable
Set rowId = incId -- both are columns in empTable
WHERE hash_id in (SELECT ID IN #InsertedIDs)

Executing a statement within a stored procedure, out parameter always returns 0

I need to insert some values into a table and to do this I created a stored procedure. 4 values are passed. And two values can be inserted straight into the table, for two other values an ID needs to be found.
I have three stored procedures. When I execute the main stored procedure, I can see that the two called stored procedures are executed and come up with the correct value. However this value is not passed into the parameter.
Both parameters #uid and #did retrun 0 (zero) into the table.
What am I doing wrong??
Kind regards,
Clemens Linders
SP MES_D_GetUserID, Pass a name and you gat an ID as string
SP MES_D_GetDOrderID, Pass a name and you get an ID as integer
The main stored procedure:
USE [AddOn_DEV_HE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[MES_D_Consumed]
#WERKS nvarchar(4), #USERNAME nvarchar(50), #MACHID int, #DRINKORDER nvarchar(50)
WITH EXEC AS CALLER
AS
BEGIN
SET NOCOUNT ON;
Declare #uid AS varchar(10)
Declare #did AS int
Declare #OUTUID AS varchar(10)
Declare #OUTDID AS int
exec #uid = MES_D_GetUserID #USERNAME, #OUTUID OUTPUT;
exec #did = MES_D_GetDOrderID #DRINKORDER, #OUTDID OUTPUT;
INSERT INTO Demo_D_Consumed (Werks, UserID, MachID, DrinkID, TimeDate) VALUES (#WERKS, #uid, #MACHID, #did, GETDATE());
END
and these are the two other stored procedures :
USE [AddOn_DEV_HE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[MES_D_GetDOrderID]
#DRINK nvarchar(50), #OUTDID int OUTPUT
WITH EXEC AS CALLER
AS
BEGIN
SET NOCOUNT ON;
SELECT RecordNr FROM DEMO_D_ORDERS WHERE Drink = #DRINK
END
USE [AddOn_DEV_HE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[MES_D_GetUserID]
#USERNAME nvarchar(50), #OUTUID nvarchar(50) OUTPUT
WITH EXEC AS CALLER
AS
BEGIN
SET NOCOUNT ON;
SELECT UserLan FROM sysUsernames WHERE UserName = #USERNAME
END
Change them to be
ALTER PROCEDURE [dbo].[MES_D_GetDOrderID]
#DRINK nvarchar(50), #OUTDID int OUTPUT
WITH EXEC AS CALLER
AS
BEGIN
SET NOCOUNT ON;
SELECT #OUTDID = RecordNr FROM DEMO_D_ORDERS WHERE Drink = #DRINK
END
And
exec MES_D_GetDOrderID #DRINKORDER, #OUTDID OUTPUT;
Your #OUTDID will have the return value. Same with the other SP.

Modify Stored Procedure Post Deployment

Is there a way to modify a stored procedure in post deployment scripts?
I am trying to modify stored procedures in a Visual Studio 2013 SQL Server Database Project and SQL Server Express 2012. I know that I can manually modify the stored procedure in the build, but depending on what branch of our application I'm working on, I need the stored procedures to change.
I've tried a number of ways to write scripts but always wind up with SQL80001 or SQL72007 around the syntax ALTER PROCEDURE or CREATE PROCEDURE. When I attempt to recreate the procedure, I do Drop it first.
The following script is being linked to Script.PostDeployment.sql.
ALTER PROCEDURE [dbo].[spCreateTemplate]
(
#name varchar(250),
#dataSourceID nvarchar(1)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id uniqueidentifier
SELECT #id = NEWID()
INSERT TemplateInfo
(
ID,
Name,
DataModeID,
DataSourceID,
StartDepth,
EndDepth,
StartDateTime,
EndDateTime,
Increment,
IsActive,
IsRealTime,
IsLogarithmic,
CreatedBy,
CreatedUTCDate,
ModifiedBy,
ModifiedUTCDate
)
VALUES
(
#id,
#name,
1,
#dataSourceID,
NULL,
NULL,
NULL,
NULL,
1,
0,
1,
0,
SUSER_SNAME(),
GETUTCDATE(),
SUSER_SNAME(),
GETUTCDATE()
)
SELECT #id
END
I have finally been able to resolve this issue. In order to alter the procedure, I had to run the stored procedure sp_executesql and pass it the script to create the stored procedure. Here is an example of what I did:
GO
SET QUOTED_IDENTIFIER ON
GO
DECLARE #sqlCmd nvarchar (4000)
IF EXISTS(select * FROM sys.procedures where name = <spName>
begin
drop procedure <spName>
SELECT #sqlCmd = 'CREATE PROCEDURE [dbo].[<spName>]
(<#variables datatype>)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id uniqueidentifier
SELECT #id = NEWID()
INSERT
<tablename>(<columns>)
VALUES
(<values>)
SELECT #ID
END'
EXEC sp_executesql #sqlCmd
END
else
begin
SELECT #sqlCmd = 'CREATE PROCEDURE [dbo].[<spName>]
(<#variables datatype>)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id uniqueidentifier
SELECT #id = NEWID()
INSERT
<tablename>(<columns>)
VALUES
(<values>)
SELECT #ID
END'
EXEC sp_executesql #sqlCmd
END
I'm also having this issue in post-deployment.
scenario:
2 files
post-deployment code
:r "file1.sql"
:r "file2.sql"
what I did to fix this is to ADD the following in every files (e.g file1.sql and fil12.sql)
...<afterENDline>
<line>
<line>
GO
<line>
I think the reason why this occur is because the post-deployment copies all the lines like :r "files1.sql" into a single .sql file.
therefore, I think you can also fix it by doing this way:
:r "file1.sql" GO
:r "file2.sql" GO
Hope this helps.

Trigger Failing when calling Stored Procedure

I am truly hoping someone can help me out...
I have a trigger to handle the insert of a new record to a table. This trigger, as you will see below, inserts a record into another table, which in turns executes a trigger on that table, that calls a stored procedure (I tried to do it within the trigger itself, but it failed and was difficult to test where it was failing, so I moved it into its own little unit.)
Within the stored procedure, there is a call to extract information from the Active Directory database (ADSI) and update the newly inserted record. However, this is where it fails when called by the trigger. When I call it by simply executing it, and passing along the record to be updated, it works great... Can anyone point me in the right direction? Please!!!
Trigger #1 in YYY
USE [YYY]
GO
/****** Object: Trigger [dbo].[NewCustodian] Script Date: 08/04/2014 09:38:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[NewCustodian]
ON [YYY].[dbo].[Custodians]
AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
DECLARE #CaseID varchar(20);
DECLARE DBcursor CURSOR FOR
SELECT [XXX].[dbo].[tblCase].CaseID from [XXX].[dbo].[tblCase] Where [XXX].[dbo].[tblCase].SQLSVR_Case_ID = 'YYY';
Open DBcursor; FETCH DBCursor into #CaseID;
CLOSE DBcursor; DEALLOCATE DBcursor;
DECLARE #NAME varchar(255);
DECLARE #TAG varchar(255);
SELECT #NAME = name FROM inserted;
SELECT #TAG = tag FROM inserted;
IF NOT EXISTS (Select eID from [XXX].[dbo].[tblNames]
WHERE eID = #TAG and CaseID = #CaseID)
BEGIN
INSERT INTO [XXX].[dbo].[tblNames] (CaseID, Name, eID)
Values (#CaseID, #NAME, #Tag);
END
END
Trigger #2 in XXX
USE [XXX]
GO
/****** Object: Trigger [dbo].[tblNames_New] Script Date: 08/04/2014 08:56:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date:
-- Description:
-- =============================================
ALTER TRIGGER [dbo].[tblNames_New]
ON [XXX].[dbo].[tblNames]
AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
DECLARE #NamesID varchar(10)
DECLARE #TAG varchar(10);
DECLARE #return_value int
SELECT #NamesID = namesID FROM inserted
EXEC dbo.UpdateNames #NamesID;
End
Stored procedure:
USE [XXX]
GO
/****** Object: StoredProcedure [dbo].[UpdateNames] Script Date: 08/04/2014 08:14:52 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [dbo].[UpdateNames]
#NamesID int
AS
BEGIN
SET FMTONLY OFF;
SET NOCOUNT ON;
DECLARE #eID varchar(10);
DECLARE #TAG varchar(10);
DECLARE #SQL nvarchar(555);
DECLARE #DBresults as table (
eID nvarchar(100),
mobile nvarchar(100),
mail nvarchar(100),
phone nvarchar(100),
name nvarchar(50),
legacyExchangeDN nvarchar(100),
Title nvarchar(100),
homeDirectory nvarchar(100));
DECLARE #mobile nvarchar(100)
DECLARE #mail nvarchar(100)
DECLARE #phone nvarchar(100) = 'Error'
DECLARE #name nvarchar(100)
DECLARE #legacyExchangeDN nvarchar(100)
DECLARE #Title nvarchar(100) = 'Error'
DECLARE #homeDirectory nvarchar(100)
SET #eID = (Select eID from [XXX].[dbo].[tblNames] Where NamesID = #NamesID)
SET #SQL = N'SELECT * FROM OpenQuery ( ADSI, ''SELECT homeDirectory,Title,legacyExchangeDN,displayName, telephoneNumber, mail, mobile,samAccountName
FROM ''''LDAP://domain.com''''
WHERE objectClass = ''''User'''' and samAccountName = ''''' + #eID+ ''''''') As tblADSI'
INSERT INTO #DBresults
EXEC sp_executesql #SQL
DECLARE DBcursor CURSOR FOR
SELECT * from #DBresults;
Open DBcursor; FETCH DBCursor into #eID, #mobile, #mail, #phone, #Name, #legacyExchangeDN, #Title, #homeDirectory;
CLOSE DBcursor; DEALLOCATE DBcursor;
UPDATE XXX.dbo.tblNames
SET Job_Title = #Title,
Phone = #Phone
Where NamesID = #NamesID;
END
As I said in my comment - a trigger should be extremely small, nimble, lean - do not do any extensive and time-consuming processing inside a trigger, and avoid anything that would cause performance bottlenecks, especially cursors!
The reason for this is the fact that a trigger will be triggered whenever an INSERT operation happens, you have no control over when and how many times it gets called. The main app will wait and hang while the trigger is at work - so therefore, don't make this a long time - return very quickly from your trigger to go on with your main app.
My approach would be:
create a new separate table where you insert some key pieces of information into from your first original trigger
CREATE TABLE NewCustodianInserted
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
CaseID VARCHAR(20),
Tag VARCHAR(255),
Handled BIT DEFAULT (0)
);
change your original trigger on the Custodians table to just insert those key pieces of information into your new "command" table:
CREATE TRIGGER [dbo].[NewCustodian]
ON [YYY].[dbo].[Custodians]
AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
-- insert key pieces about the new custodian into "command" table
INSERT INTO dbo.NewCustodianInserted (CaseID, Tag)
SELECT i.CaseId, i.Tag
FROM Inserted i
WHERE NOT EXISTS (SELECT * FROM [XXX].[dbo].[tblNames] WHERE eID = i.Tag AND CaseID = i.CaseID)
END
in a separate process, e.g. a SQL Server Agent job that is scheduled to run every 5 mînutes (or whatever makes sense for your application), read the "command" table, get the new custodians to handle, call that long-running stored procedure updating Active Directory from it. Here, since this runs asynchronously from your main application, it's ok to use a cursor which you almost have to since you want to call a stored procedure for every row in your new table.
CREATE PROCEDURE HandleNewCustodians
AS
BEGIN
SET NOCOUNT ON;
DECLARE #CaseID VARCHAR(20);
DECLARE #Tag VARCHAR(255);
DECLARE #NamesID varchar(10);
DECLARE CustodianCursor CURSOR FAST_FORWARD
FOR
SELECT CaseID, Tag FROM dbo.NewCustodianInserted WHERE Handled = 0
OPEN CustodianCursor
FETCH NEXT FROM CustodianCursor INTO #CaseID, #Tag;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #NamesID = NameID
FROM [XXX].[dbo].[tblNames] WHERE eID = #Tag AND CaseID = #CaseID
EXEC dbo.UpdateNames #NamesID;
FETCH NEXT FROM CustodianCursor INTO #CaseID, #Tag;
END
CLOSE CustodianCursor;
DEALLOCATE CustodianCursor;
END