Stored procedure for login not working as intended - sql

I have 3 tables, users, Roles, and User_Roles.
CREATE TABLE [Web].[Users]
(
[Employee_ID] [Nvarchar] (10) NOT NULL PRIMARY KEY,
[Username] [nvarchar] (25) NOT NULL,
)
GO
CREATE TABLE [Web].[Roles]
(
[Role_ID] [Int] NOT NULL IDENTITY PRIMARY KEY,
[Roles] [nvarchar] (25) NOT NULL,
)
GO
CREATE TABLE [Web].[User_Roles]
(
[Employee_ID] [Nvarchar](10) NOT NULL,
[Role_ID] [int] NOT NULL,
CONSTRAINT FK_Employee_ID
FOREIGN KEY (Employee_ID) REFERENCES [Web].[Users] (Employee_ID)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_Role_ID
FOREIGN KEY (Role_ID) REFERENCES [Web].[Roles] (Role_ID),
CONSTRAINT pk_User_Roles PRIMARY KEY (Employee_ID, Role_ID)
);
The way it works is, when you login, it checks your Username against the Employee_ID in Users table, then goes to the User_Roles table and matches the Employee_ID it got from the Users table to the User_Roles table. Then it gets the Role_ID from the User_Roles table and matches the Role_ID to the roles in the Roles table.
I'm trying to write a stored procedure that does the checks and so far I've gotten close, but I feel I'm missing something. Hoping I can get help with it. This is what I have so far.
CREATE PROCEDURE [Web].[Get_User_Roles]
#Username NVARCHAR(25)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Employee_ID Nvarchar, #Role_ID INT
SELECT #Employee_ID = #Employee_ID, #Role_ID = #Role_ID
FROM [Web].[Users]
WHERE #Employee_ID = #Employee_ID AND #Role_ID = #Role_ID
IF #Employee_ID IS NOT NULL
BEGIN
IF NOT EXISTS (SELECT #Employee_ID FROM [Web].Users
WHERE #Username = Username)
BEGIN
SELECT
#Employee_ID [Employee_ID],
(SELECT #Role_ID FROM User_Roles
WHERE #Role_ID = #Role_ID) [Roles]-- User Valid
END
ELSE
BEGIN
SELECT -2 [#Username], '' [Roles]--User not activated
END
END
ELSE
BEGIN
SELECT -1 [#Username], '' [Roles]-- User invalid
END
END
Any help to improve this would be appreciated. I put the tables code above so that it's available. My final outcome I shall ask again in this section as well as the middle. The way it works is, when you login, it checks your Username against the Employee_ID in Users Table, then goes to the User_Roles table and matches the Employee_ID it got from the Users table to the User_Roles table. Then it gets the Role_ID from the User_Roles table and matches the Role_ID to the Roles in the Roles table. I'm trying to make a stored procedure that does the checks and so far I've gotten close, but I feel I'm missing something. Hoping I can get help with it. This is what I have so far.

First of all, this part is not going to work at all:
select #Employee_ID = #Employee_ID, #Role_ID = #Role_ID
from [Web].[Users] WHERE #Employee_ID = #Employee_ID and #Role_ID = #Role_ID
You are comparing variables with themselves in the WHERE Statement, you are assigning variables to each other, and you have no Role_ID in the [Web].[Users] table. Try the following
SELECT Employee_ID, Roles
FROM [Web].[Users] A
INNER JOIN [Web].[User_Roles] B
ON A.[Employee_ID] = B.[Employee_ID]
INNER JOIN [Web].[Roles] C
ON b.[Role_ID] = C.[Role_ID]
WHERE [Username] = #Username
This code will return the Employee_id and Roles if you have a user with that username, or an empty data set otherwise.

Still not very clear what you want as output but I think it would be something like this. This will return -2 in the UserStatus column if there isn't a role for the user. Otherwise it will return 1 in that column and the Role. You should just check if there are rows returned when you call this. If it returns no rows then the user doesn't exist.
select UserStatus = case when ur.Role_ID is null then -2 else 1 end
, r.Roles
from Web.Users u
left join Web.User_Roles ur on ur.Employee_ID = u.Employee_ID
left join Web.Roles r on r.Role_ID = ur.Role_ID
where u.Username = #Username

Related

trigger for not inserting members doesnt work

I have this table
CREATE TABLE members
(
member_id INT PRIMARY KEY NOT NULL,
first_name VARCHAR(20),
last_name VARCHAR(20),
web_page VARCHAR(200),
e_mail VARCHAR(200),
cv VARCHAR(800),
dep_id INT,
teacher_id INT
);
and I want to create a trigger that if someone wants to insert a member which has a dep_id of 1 or 2 or 3.
And the teacher_id is different than NULL (as the teacher_id column is filled with either NULL or an id of another member)
I came up with this
CREATE TRIGGER employee_insup1
ON members
FOR INSERT, UPDATE
AS
DECLARE #dep_id INT, #teacher_id INT
SELECT #dep_id = i.dep_id, #teacher_id = i.teacher_id
FROM inserted i
IF ((#dep_id = 1) AND (#teacher_id != NULL))
BEGIN
RAISERROR('Teacher_id expects NULL',16,1)
ROLLBACK TRANSACTION
END
but after all if I try to insert a member with dep_id 1 and teacher_id 7(for example) it will be registered
You don't need a trigger for this. A check constraint is sufficient:
alter table members add constraint chk_members_dep_teacher
check (dep_id not in (1, 2, 3) or teacher_id is not null);
Specifically, this ensures that when dep_id is in one of those departments, then the teacher_id is not null. You might find the logic easier to follow as:
alter table members add constraint chk_members_dep_teacher
check (not (dep_id nt in (1, 2, 3) and teacher_id is null) );

Insert into multiple(7) tables with no duplicates

Trying to create a query/SP that will take data from one table and insert it into multiple tables. I have one main table that everything is put into at the beginning like a temp table.
Temp table
CREATE TABLE Employee
(
userID INT IDENTITY(1,1) NOT NULL,
userName VARCHAR(50) NULL,
FirstName VARCHAR(50) NULL,
LastName VARCHAR(50) NUll,
UserPassWd VARCHAR(50) NULL,
EmailId VARCHAR(100) NULL
CONSTRAINT PK_Employee PRIMARY KEY (userID)
)
Than when employee is verified it will be split up into multiple tables that only need a field or two from the temp table as needed. The UserEmail table I have listed below is one of the tables. I'm trying to get it to work for one table right now and then I'm guessing i will just copy the insert part and change the table name and attributes to the new tables
Here is what i have so far.
DECLARE #EMAIL VARCHAR(100)
DECLARE #USERID INT
SELECT #USERID = userID
,#EMAIL = EmailId
FROM Employee
WHERE userID = 1004
INSERT INTO UserEmail
(
EmailAddress
,EmailTypeID
,ExternalUserID
,Active
,CreatedByID
,CreatedDate
,UpdatedByID
,UpdatedDate
)
SELECT #EMAIL -- Email Address
,1 -- Email Type
,1 -- ExternalUserID
,1 -- Active
,1 -- CreatedByID
,CURRENT_TIMESTAMP -- CreatedDate
,1
,CURRENT_TIMESTAMP -- UpdatedDate
FROM Employee X
WHERE 1=1
AND X.userID = '####'-- INSERT USERID HERE for testing
This will insert the record into the UserEmail table but will create duplicate users, which i cant have so I tried adding this but it doesn't do what I want it to do.
WHERE 1=1
AND NOT EXISTS(
SELECT userID
FROM Employee
WHERE userID = 1004
)
Any guidance or help would be much appreciated. Thank You!
If you only like NOT to insert to UserEmail if user already exists just extend
INSERT INTO UserEmail ....
SELECT ....
FROM ....
WHERE ..
AND NOT EXISTS (select 1 from UserEmail where EmailAddress = X.emailAddress)
Otherwise review MERGE syntax (https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql)

How to save auto generated primary key Id in foreign key column in same table

Following is the table structure:
CREATE TABLE [User] (
[Id] bigint identity(1,1) not null,
[FirstName] nvarchar(100) not null,
[LastName] nvarchar(100) not null,
[Title] nvarchar(5) null,
[UserName] nvarchar(100) not null,
[Password] nvarchar(100) not null,
[Inactive] bit null,
[Created] Datetime not null,
[Creator] bigint not null,
[Modified] DateTime null,
[Modifier] bigint null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[Id] Asc
)
);
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO
INSERT INTO [User] (Creator) Values ([Id] ?)
This is a case when table is empty and first user is going to add in table. Otherwise I don't have issue.
How can I insert Id in creator column with insert statement at the same time?
One way could be using Sequence instead of identity column. The below script might serve the same purpose:
CREATE SEQUENCE dbo.useridsequence
AS int
START WITH 1
INCREMENT BY 1 ;
GO
CREATE TABLE [User] (
[Id] bigint DEFAULT (NEXT VALUE FOR dbo.useridsequence) ,
[FirstName] nvarchar(100) not null,
[LastName] nvarchar(100) not null,
[Title] nvarchar(5) null,
[UserName] nvarchar(100) not null,
[Password] nvarchar(100) not null,
[Inactive] bit null,
[Created] Datetime not null,
[Creator] bigint DEFAULT NEXT VALUE FOR dbo.useridsequence ,
[Modified] DateTime null,
[Modifier] bigint null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[Id] Asc
)
);
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO
INSERT INTO [User]
(
-- Id -- this column value is auto-generated
FirstName,
LastName,
Title,
UserName,
[Password],
Inactive,
Created,
Creator,
Modified,
Modifier
)
VALUES
(
'Foo',
'Bar',
'Title',
'UserName ',
'Password',
0,
GETDATE(),
DEFAULT,
GETDATE(),
1
)
SELECT * FROM [User] AS u
Result :
The short answer is that you can't do this. And I suggest your model is logically flawed in the first place. Do you intend to define all actual database users (e.g., create user ... for login ...) as rows in [Users]? You need to think about that - but the typical answer is no. If the answer is yes, then you don't need the creator column at all because it is redundant. All you need is the created date - for which you probably should have defined a default.
But if you want to do this, you will need to do it in two steps (and you will need to make the column nullable). You insert a row (or rows) with values for the "real" data columns. Then update those same rows with the identity values generated for id. An example showing different ways to do this
use tempdb;
set nocount on;
CREATE TABLE dbo.[user] (
[user_id] smallint identity(3,10) not null primary key,
[name] nvarchar(20) not null,
[active] bit not null default (1),
[created] Datetime not null default (current_timestamp),
[creator] smallint null
);
ALTER TABLE dbo.[user] ADD CONSTRAINT [fk_user] FOREIGN KEY(creator) REFERENCES dbo.[user](user_id);
GO
-- add first row
insert dbo.[user] (name) values ('test');
update dbo.[user] set creator = SCOPE_IDENTITY() where user_id = SCOPE_IDENTITY()
-- add two more rows
declare #ids table (user_id smallint not null);
insert dbo.[user] (name) output inserted.user_id into #ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from #ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;
select * from dbo.[user] order by user_id;
-- mess things up a bit
delete dbo.[user] where name = 'pom';
-- create an error, consume an identity value
insert dbo.[user](name) values (null);
-- add 2 morerows
delete #ids;
insert dbo.[user] (name) output inserted.user_id into #ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from #ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;
select * from dbo.[user] order by user_id;
drop table dbo.[user];
And I changed the identity specification to demonstrate something few developers realize. It isn't always defined as (1,1) and the next inserted value can jump for many reasons - errors and caching/restarts for example. Lastly, I think you will regret naming a table with a reserved word since references to it will require the use of delimiters. Reduce the pain.

How to update foreign keys in single one statement

I want to update foreign keys into a table named User where foreign keys refer to a Project table, a Group table.
The user belongs to a project, within a group, the group are defined to the project level. The detailed user information are also stored into a table ToolUser
The create statement for the User table is as following:
CREATE TABLE [dbo].[User](
[ID] [int] IDENTITY(1,1) NOT NULL,
[IDproject] [int] NOT NULL,
[IDtoolUser] [int] NOT NULL,
[IDgroup] [int] NOT NULL,
[datemodified] [datetime] NOT NULL DEFAULT (getdate())
IDproject is FK for ID in Project table
IDtoolUser is FK for ID in ToolUser table
IDgroup is FK for ID in Group table
The update I want to perform consist in changing the group the user is assigned to which requires to resolve the project ID, the group ID before updating the groupID in the User table
I came up with a solution where the group id is calculed with a function, [dbo].[IDofGroup], and used into an UPDATE statement as following
UPDATE [dbo].[User]
SET IDgroup = [dbo].[IDofGroup]('TESTPROJ','none')
FROM [dbo].[User] as u
INNER JOIN [dbo].[Group] g
ON g.[ID]= u.[IDgroup]
INNER JOIN [dbo].[Project] p
ON p.[ID]= u.[IDproject]
AND p.[name] ='TESTPROJ'
WHERE g.[name] = 'TESTGROUP'
GO
The function IDofGroup itself performs couple SELECT to resolve project ID and group ID as following
ALTER FUNCTION [dbo].[IDofGroup]
(
-- Add the parameters for the function here
#ProjectName as varchar(max),
#GroupName as varchar(max)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #ProjectID int, #ResultID int
SET #ResultID=0
-- get project ID for #ProjectName
SELECT #ProjectID= ID
FROM [dbo].[Project]
WHERE [name]=#ProjectName
-- find id fro group which name is #GroupName and project ID #ProjectID
SELECT #ResultID= ID
FROM [dbo].[Group]
WHERE [IDproject]=#ProjectID
AND [name]=#GroupName
-- Return the result of the function
RETURN #ResultID
END
I m wondering if there any way to perform the UPDATE with some kind of JOIN in a single UPDATE statement w/o the need of using of function IDofGroup or other SELECT statements before the UPDATE statement
Try this one:
UPDATE u
SET IDgroup = g1.[ID]
FROM [dbo].[User] as u
INNER JOIN [dbo].[Project] p
ON p.[ID]= u.[IDproject]
INNER JOIN [dbo].[Group] g
ON g.[ID]= u.[IDgroup]
INNER JOIN [dbo].[Group] g1
ON g.[IDproject] = g1.[IDproject]
WHERE p.[name] ='TESTPROJ'
AND g.[name] = 'TESTGROUP'
AND g1.[name] = 'none'

One Active Record per ID

The address table is shared for both companies and contacts. I have a constraint that prevents the user from adding a record with both a Company ID and a Contact ID. I am trying to add another constraint to have only 1 active (ACTIVE FLAG = 'TRUE') address per Company ID or Contact ID. But I want to be able to have unlimited inactive (ACTIVE FLAG = 'FALSE').
ALTER TABLE [dbo].[ADDRESSES] WITH CHECK ADD CONSTRAINT [chk_ONLY_ONE_ACTIVE_ADDRESS] CHECK (([COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL AND [ACTIVE] = 'TRUE' OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL AND [ACTIVE] = 'TRUE' OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL AND [ACTIVE] = 'FALSE' OR [COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL AND [ACTIVE] = 'FALSE'))
GO
Where am I missing it?
Thanks
jlimited
You are adding the constraint 'active=true' and 'active=false' for both combinations of contact id and company id so it's a tautology, you may as well remove this constraint.
I believe the only way to enforce the rule 'only one active flag and 0 to many inactive' is by triggers.
Thanks for the reply Jayvee. Here is the solution that I figured out.
I first created two functions...
One
CREATE FUNCTION [dbo].[fnCheckForActiveContactAddress](
#id int
)
RETURNS INT
AS
BEGIN
DECLARE #result INT
IF #id IS NOT NULL
SET #result = ISNULL((select count(*) from dbo.Addresses where CONTACT_ID = #ID AND ACTIVE = 'TRUE'),0)
ELSE
SET #result = 1
RETURN #result
END
GO
Two
CREATE FUNCTION [dbo].[fnCheckForActiveCompanyAddress](
#id int
)
RETURNS INT
AS
BEGIN
DECLARE #result INT
If #id IS NOT NULL
SET #result = ISNULL((select count(*) from dbo.Addresses where COMPANY_ID = #ID AND ACTIVE = 'TRUE'),0)
ELSE
SET #result = 1
RETURN #result
END
GO
Then I added the following constraints...
ALTER TABLE [dbo].[ADDRESSES] WITH CHECK ADD CONSTRAINT [chk_COMPANY_OR_CONTACT] CHECK (([COMPANY_ID] IS NOT NULL AND [CONTACT_ID] IS NULL OR [COMPANY_ID] IS NULL AND [CONTACT_ID] IS NOT NULL))
GO
ALTER TABLE [dbo].[ADDRESSES] WITH NOCHECK ADD CONSTRAINT [CHK_ADDRESSES_ONLY_ONE_ACTIVE_CONTACT] CHECK (([dbo].[fnCheckForActiveContactAddress]([CONTACT_ID])=(1)))
GO
ALTER TABLE [dbo].[ADDRESSES] WITH NOCHECK ADD CONSTRAINT [CHK_ADDRESSES_ONLY_ONE_ACTIVE_COMPANY] CHECK (([dbo].[fnCheckForActiveCompanyAddress]([COMPANY_ID])=(1)))
GO
This solution seems to work well.
Any thoughts on improving it?
jlimited
If I understand your requirements, this should work (if -1 is not a valid CompanyID or ContactID):
create table T (
CompanyID int,
ContactID int,
BothIDs as
CASE WHEN CompanyID IS NOT NULL and ContactID IS NOT NULL
THEN 1 ELSE 0 END PERSISTED
check (BothIDs = 0),
ActiveFlag varchar(5) check (ActiveFlag in ('TRUE','FALSE')),
ActiveCheck as
CASE WHEN ActiveFlag='TRUE' then -1 ELSE COALESCE(CompanyID,ContactID) END,
unique (ActiveCheck)
);
insert into T values
(1,NULL,'FALSE'),
(NULL,2,'FALSE'),
(3,NULL,'TRUE'),
(4,NULL,'FALSE');
GO
UPDATE T SET
ActiveFlag = 'TRUE'
WHERE CompanyID = 4;