I've tried a lot of googling and following some tutorials. I'm using MS SQL and trying to make a simple User table and salt and hashing the password, haven't gotten to the salting part, yet.
The table looks like this:
CREATE TABLE dbo.[User] (
UserID INT IDENTITY(1,1) NOT NULL,
Email NVARCHAR(45) NOT NULL,
Password BINARY(64) NOT NULL,
FirstName NVARCHAR(45) NULL,
LastName NVARCHAR(45) NULL,
CONSTRAINT [PK_User_UserID] PRIMARY KEY CLUSTERED (UserID ASC)
)
And the procedure looks like this:
ALTER PROCEDURE dbo.[uspAddUser]
#pEmail NVARCHAR(45),
#pPassword NVARCHAR(45),
#pFirstName NVARCHAR(45) = NULL,
#pLastName NVARCHAR(45) = NULL,
#responseMessage NVARCHAR(250) OUTPUT
AS
BEGIN
SET NOCOUNT ON
BEGIN TRY
INSERT INTO dbo.[User] (Email, Password, FirstName, LastName)
VALUES (#pEmail, HASHBYTES('SHA_512', #pPassword), #pFirstName, #pLastName)
SET #responseMessage = 'Success'
END TRY
BEGIN CATCH
SET #responseMessage = ERROR_MESSAGE()
END CATCH
END
When I try to insert with the procedure it won't work:
DECLARE #responseMessage NVARCHAR(250)
EXEC dbo.[uspAddUser]
#pEmail = N'Admin#email.com',
#pPassword = N'123',
#pFirstName = N'Admin',
#pLastName = N'Administrator',
#responseMessage=#responseMessage OUTPUT
SELECT *
FROM [dbo].[User]
No results are shown. After inserting normally and then retrieving the results it shows the newly created tuple but with the ID auto incremented like other tuples were created. I'm not really sure where the problem is and would be grateful if someone else understood.
EDIT:
SOLVED
Apparently the fault was I used the wrong kind of sha hash. Instead of using SHA_512 I should've used SHA2_512.
HASHBYTES('SHA_512', #pPassword) returns null so that insert query gets aborted. Instead, you can use algorithms SHA2_512 or SHA2_256 or any other encryption algorithm you want.
Check out this link for more info.
Related
Is it possible to write a procedure in SQL Server that, before executing INSERT into a table, calls a function to check the format of email (1/0)?
INSERT will be executed only if the result of the checkemail function is 1, and in case of 0 it will return the error 'email is not valid'.
How it's done?
CheckEmail function:
CREATE FUNCTION dbo.CheckEmail
(#Email VARCHAR(100))
RETURNS BIT
AS
BEGIN
DECLARE #Result BIT
IF #Email IS NOT NULL
BEGIN
IF #Email LIKE '%["<>'']%'
OR #Email NOT LIKE '%#%.%'
OR #Email LIKE '%..%'
OR #Email LIKE '%#%#%'
OR #Email LIKE '%.#%'
OR #Email LIKE '%#.%'
OR #Email LIKE '%.cm'
OR #Email LIKE '%.co'
OR #Email LIKE '%.OR'
OR #Email LIKE '%.ne'
OR #Email LIKE '#%'
SET #Result = 0;
ELSE IF #Email LIKE '%_#__%.__%'
AND PATINDEX('%[^a-z,0-9,#,.,_,\-]%', #Email) = 0
SET #Result = 1;
END
RETURN #Result;
END
Users table:
CREATE TABLE Users
(
"Id" UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
"Email" VARCHAR(40) NOT NULL UNIQUE,
"Username" VARCHAR(30) NOT NULL,
"Password" VARCHAR(30) NOT NULL,
"Name" NVARCHAR(50) NOT NULL,
"Surname" NVARCHAR(50) NOT NULL
)
You can use IF statement like that :
IF CheckEmail('my#email.com')
INSERT INTO Users VALUES (...)
Also, you can add an ELSE statement if you want to do something with the line that doesn't correspond to what you want.
Documenation
There are several approaches, depending on your scenario, which you don't elaborate on. Assuming SQL Server given the SSMS tag.
If you are passing data to a stored procedure to handle a single insert, simply use if/then logic:
if dbo.CheckEmail(#email)=1
begin
/* Insert data here */
end
else
begin
/* raise any warnings / return error */
end
If you are inserting rows by selecting data from a source location, use a where criteria
insert into table Users (Email, Username, Password...)
select t1.Email, t1.Username, t1.Password...
from table t1
where dbo.CheckEmail(t1.email)=1
The most performant way would be to create a table-valued function instead of a scalar function which you can then use with cross apply
insert into table Users (Email, Username, Password...)
select t1.Email, t1.Username, t1.Password...
from table t1
cross apply dbo.CheckEmail(t1.email)ck
where ck.result=1
You are probably much better off doing this as a CHECK constraint
ALTER TABLE Users
ADD CONSTRAINT EmailCheck CHECK (
NOT( Email LIKE '%["<>'']%'
OR Email NOT LIKE '%#%.%'
OR Email LIKE '%..%'
OR Email LIKE '%#%#%'
OR Email LIKE '%.#%'
OR Email LIKE '%#.%'
OR Email LIKE '%.cm'
OR Email LIKE '%.co'
OR Email LIKE '%.OR'
OR Email LIKE '%.ne'
OR Email LIKE '#%'
) AND
Email LIKE '%_#__%.__%'
AND PATINDEX('%[^a-z,0-9,#,.,_,\-]%', Email) = 0
);
Maybe someone has an idea what I can check:
using (var cnn = new SqlConnection(connection))
{
cnn.Open();
using (var cmd = new SqlCommand())
{
cmd.Connection = cnn;
cmd.CommandText = "IF EXISTS (SELECT * FROM kruserprofile WHERE lactorid=#1 AND strname=#2) " +
" UPDATE kruserprofile SET txtvalue=#3 WHERE lactorid=#1 AND strname=#2 " +
"ELSE " +
" INSERT INTO kruserprofile (lactorid, strname, txtvalue) " +
" VALUES (#1, #2, #3)";
cmd.CommandTimeout = 120;
cmd.Parameters.AddWithValue("#1", actorId);
cmd.Parameters.AddWithValue("#2", ident);
cmd.Parameters.AddWithValue("#3", _mXml.ToString());
cmd.ExecuteNonQuery();
}
cnn.Close();
}
As you can see in this piece of code I do check if records exists and update or insert.
This works pretty fine on SQL Server systems.
On Azure SQL I do sometimes but not reproduce able the error:
Violation on PRIMARY KEY... where the constraint is on lactorid and strname.
Table:
CREATE TABLE dbo.kruserprofile (
lactorid int NOT NULL,
strname nvarchar(254) NOT NULL,
txtvalue nvarchar(max) NULL
);
GO
ALTER TABLE dbo.kruserprofile ADD CONSTRAINT PK__kruserpr__6E092EE804688C07 PRIMARY KEY (lactorid, strname);
GO
So I did not find any reason why, but sometimes it seems that the EXISTSreturns false so it try to insert which fails then. Any idea what I can check?
update:
Solution1:
We also have found the solution here.
Solution2:
I've created a test about using MERGE in Azure SQL.
Create table:
CREATE TABLE dbo.kruserprofile (
lactorid int NOT NULL,
strname nvarchar(254) NOT NULL,
txtvalue nvarchar(max) NULL
);
GO
ALTER TABLE dbo.kruserprofile ADD CONSTRAINT PK__kruserpr__6E092EE804688C07 PRIMARY KEY (lactorid, strname);
GO
Create a Table-valued parameter named dbo.kruserprofile_type, it will be used in my stored procedure:
create TYPE dbo.kruserprofile_type AS TABLE(
lactorid int NOT NULL,
strname nvarchar(254) NOT NULL,
txtvalue nvarchar(max)
)
GO
Create a Stored procedure, it will merge the same records and insert new records based on the primary key:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[spUpsertKruserprofile]
#profile dbo.kruserprofile_type READONLY
AS
BEGIN
MERGE dbo.kruserprofile AS target_sqldb
USING #profile AS source_tblstg
ON (target_sqldb.lactorid = source_tblstg.lactorid and target_sqldb.strname = source_tblstg.strname )
WHEN MATCHED THEN
UPDATE SET
txtvalue = source_tblstg.txtvalue
WHEN NOT MATCHED THEN
INSERT (
lactorid,
strname,
txtvalue
)
VALUES (
source_tblstg.lactorid,
source_tblstg.strname,
source_tblstg.txtvalue
);
END
GO
After that, we can execute the stored procedure by following code:
DECLARE #profileVar AS dbo.kruserprofile_type;
/* Add data to the table variable. */
INSERT INTO #profileVar (lactorid, strname, txtvalue) values (1, 'tom','wednesday');
exec [dbo].[spUpsertKruserprofile] #profileVar
That's all.
I'm getting this error when accessing a stored procedure in SQL Server:
Procedure or function 'resetdata' expects parameter '#FirstName', which was not supplied.
This is my table:
CREATE TABLE dbo.Client
(
ClientID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
StreetAddress VARCHAR(50) NULL,
Suburb VARCHAR(15)NULL,
C_State VARCHAR(3) NULL, --CHECK (C_State IN ('QLD', 'NSW', 'VIC', 'TAS', 'SA', 'WA', 'NT', 'ACT')),
PostCode SMALLINT NOT NULL,
PhoneNumber VARCHAR(11) NULL,
);
GO
ALTER TABLE Client
ADD CONSTRAINT state_ CHECK (C_State IN ('QLD', 'NSW', 'VIC', 'TAS', 'SA', 'WA', 'NT', 'ACT')),
CONSTRAINT check_post_code CHECK ([PostCode] LIKE '[0-9][0-9][0-9][0-9]' or [PostCode] LIKE '[0-9][0-9][0-9]');
GO
This is my stored procedure:
CREATE PROCEDURE [dbo].[resetdata]
#FirstName varchar(50),
#LastName varchar(50),
#PostCode smallint
AS
BEGIN
INSERT INTO A1.dbo.Client (FirstName, LastName, PostCode)
VALUES(#FirstName, #LastName, #PostCode)
END;
GO
EXEC dbo.resetdata;
INSERT INTO Client(FirstName, LastName, PostCode)
VALUES ('F', 'L', 12345);
Error message means exactly what it says: your SP is expecting three arguments as declared.
This is your SP header definition:
create procedure [dbo].[resetdata]
#FirstName varchar(50),
#LastName varchar(50),
#PostCode smallint
Three arguments. None of them has default.
This is how you're attempting to execute it:
EXEC dbo.resetdata;
No arguments provided. This causes the error from your question. Pass arguments to avoid error message.
Couple of things:
if you will try to apply script of your SP to test database or another copy of your database, you will have to modify your first line with USE A1. I'd suggest to remove this line. Choose DB once you connect, then just run all scripts on current database
Insert INTO A1.dbo.Client - same note about different databases and one more - if you already defined database with USE A1 at the beginning of the script, why are you referring same DB explicitly? If your SP and table are supposed to be in same DB - don't specify DB.
There are a couple of issues here:
The EXEC procedure is not passing the parameters required.
There is a constraint on the Postcode that will fail with 12345.
The correct code to run your stored procedure would be:
EXEC [dbo].[resetdata]
#FirstName = 'F',
#LastName = 'L',
#PostCode = 123
Try this:
USE A1;
GO
create procedure [dbo].[resetdata]
#FirstName varchar(50),
#LastName varchar(50),
#PostCode smallint
AS
BEGIN
Insert INTO A1.dbo.Client (FirstName,LastName,PostCode)
VALUES(#FirstName,#LastName,#PostCode)
END;
GO
EXEC dbo.resetdata #FirstName='F', #LastName='L', #PostCode=12345
--INSERT INTO Client(FirstName,LastName,PostCode) VALUES ('F','L',12345); No need to this Insert
Note: In sample data, you are going to insert 12345 to PostCode column, So it seems that 5 digit numbers are valid, but you have check constraint that shows PostCode should be 3 or 4 digit length. If 5 digit is valid so pay attention to the PostCode column's data type as it's maximum value is 32767. So if you want to insert a record with PostCode greater than this value you will get an error. May be it would be better to define it as INT and control it's value via a Check Constraint and also change the current check constraint definition on this column.
I am new to SQL Server and T-SQL, but I do have some experience building applications in MS Access.
This stored procedure runs fine when I execute it from the application, however when I am debugging it in SSMS, I get an error
Unable to Step. Invalid Operation.
but it will allow me to step through. Based on my research, it seems like I am creating a race condition but I have not been able to correctly fix the issue. I would also appreciate any advice to optimize this or fix any issues that are apparent.
What the code does:
This code is to enter a new customer into the database. The first select statement looks for an existing ID. If the ID is null, it will add a new customer. If the ID is not null, it will not add the customer. The same goes for the second IF statement to check if #pName2 and #pName3 are null before inserting these values.
Here is my code:
#pUID nvarchar(16) = null,
#pName1 nvarchar(50) = null,
#pName2 nvarchar(50) = null,
#pName3 nvarchar(50) = null,
#pAddress1 nvarchar(30) = null,
#pAddress2 nvarchar(30) = null,
#pAddress3 nvarchar(30) = null,
#pZipCode nvarchar(30) = null
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ID INT
SELECT #ID = ID FROM tblCustomer WHERE strUID = #pUID
IF #ID IS NULL
BEGIN
DECLARE #Customer_ID INT
INSERT INTO tblCustomer(strUID, strName)
VALUES(#pUID, #pName1)
SET #Customer_ID = ##IDENTITY
IF (#pName2 <> '') OR (#pName3 <> '')
BEGIN
INSERT INTO tblSecondaryCustomer(CustomerID, strName2, strName3)
VALUES(#Customer_ID, #pName2, #pName3)
END
INSERT INTO tblAddress(CustomerID, strAddress1, strAddress2, strAddress3, strZipCode)
VALUES(#Customer_ID, #pAddress1, #pAddress2, #pAddress3, #pZipCode)
END
END
Try replacing your IF statement with the following:
IF NOT EXISTS(SELECT ID FROM tblCustomer WHERE strUID = #pUID)
It doesn't seem your using #ID other than a check for existence...and you can use the ISNULL function to make sure you cover NULL cases...
IF (ISNULL(#pName2,'') <> '') OR (ISNULL(#pName3,'') <> '')
HTH
Dave
My insert procedure is working fine the way i want. But update is not working with scope identity.
SET ANSI_NULLS ON
ALTER PROCEDURE [dbo].[spr_unitCreation]
(
#Unit_Name VARCHAR(50) = NULL,
#Unit_Abbreviation VARCHAR(50) = NULL,
#Unit_type VARCHAR(50) = NULL,
#Decimal_Places VARCHAR(50) = NULL,
#Description VARCHAR(50) = NULL,
#Super_Unit VARCHAR(50) = NULL,
#Per_Unit VARCHAR(50) = NULL,
#unit_Id INT OUTPUT,
#abc VARCHAR(50) = NULL
)
AS
BEGIN
IF #abc = 'update' BEGIN
SET NOCOUNT ON;
SELECT #unit_Id AS SCOPE_IDENTITY
UPDATE tbl_UnitCreation
SET Unit_Name = #Unit_Name,
Unit_Abbreviation = #Unit_Abbreviation,
Unit_type = #Unit_type,
Decimal_Places = #Decimal_Places,
Description = #Description,
Super_Unit = #Super_Unit,
Per_Unit = #Per_Unit
WHERE unit_Id = #unit_Id
END
END
SELECT * FROM tbl_UnitCreation
SCOPE_IDENTITY returns the last identity value inserted into an identity column. You are not inserting a row.
To update a row all you need is to pass a value to #unit_Id when executing [spr_unitCreation]. Also remove the line "SELECT #unit_Id AS SCOPE_IDENTITY" from your code.
Based on the comments, you need to find the correct id by searching on relevant details. So you can get the id like this:
SELECT #unit_Id = unit_Id
FROM tbl_UnitCreation
WHERE Unit_Name=#Unit_Name -- NB: Ensure this column contains your relevant details
Another commonly used option is to use the OUTPUT clause of the UPDATE statement, inserting all the updated/"inserted" primary keys and Unit_name into a tablevariable.
https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-2017