Update table using stored procedure in SQL Server 2014 - sql-server

I have two tables, please see attached schema, and I have written a stored procedure which is not working. Please help me.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[countries]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[Prefix] [nvarchar](max) NULL,
[MinDigits] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[customers]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[Business] [nvarchar](max) NULL,
[Phone] [nvarchar](max) NULL,
[CountryID] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Sample data in the tables:
Id Name Business Phone CountryID
---------------------------------------------------
1 John B&B LLC 001820199202 ***
2 Mike ABC Inc 006192479121 ***
3 Jennifer Coca Cola 0017421 ***
4 Sabine ABC Inc 0091827411 ***
...
Countries:
Id Name Prefix MinDigits
--------------------------------------
1 USA 001 10
2 Australia 0061 11
3 India 0091 9
...
I want to update the CountryID in the customers table based on the countries table.
Result based on above:
Id Name Business Phone CountryID
---------------------------------------------------
1 John B&B LLC 001820199202 1
2 Mike ABC Inc 006192479121 2
3 Jennifer Coca Cola 0017421 ***
4 Sabine ABC Inc 0091827411 3
...
The entry for Jennifer is not updated, since minimum digits are not matching
This is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[UpdateCustomer]
AS
BEGIN
SELECT *
INTO #CustomerTable
FROM dbo.customers
DECLARE #Phone NVARCHAR
DECLARE #CountryID INT
DECLARE #Count INT
DECLARE #CustomerID INT
WHILE EXISTS (SELECT * FROM #CustomerTable)
BEGIN
SELECT TOP 1
#Phone = Phone, #CustomerID = Id
FROM
#CustomerTable
IF((SELECT TOP 1 COUNT(*)
FROM countries
WHERE #Phone LIKE Prefix+'%' And LEN(#Phone) <= MinDigits) > 0)
BEGIN
SELECT TOP 1
#CountryID = Id
FROM
countries
WHERE
#Phone LIKE Prefix + '%'
AND LEN(#Phone) <= MinDigits
UPDATE customers
SET CountryID = #CountryID
WHERE Id = #CustomerID
END
DELETE #CustomerTable
WHERE Id = #CustomerID
END
DROP TABLE #CustomerTable
END

You could just do something like this as your entire stored procedure
UPDATE cu
SET cu.CountryId = co.Id
FROM customers cu
JOIN countries co ON cu.phone LIKE co.prefix + '%'
WHERE LEN(cu.phone) >= co.MinDigits

Still, If you want to do it from your SP then define size of #phone variable as
DECLARE #Phone NVARCHAR(30) -- as needed
Its not holding assigned phone number so the update statement is not working.

First of all SQL is set based, so using loop concept is not best idea.
You could use MERGE statement:
MERGE customers c
USING countries cs
ON c.Phone LIKE CONCAT(cs.Prefix, '%')
AND LEN(c.Phone) >= cs.MinDigits
WHEN MATCHED THEN
UPDATE
SET CountryId = cs.Id;
LiveDemo

Use UPDATE FROM
UPDATE customers
SET CountryID = C.Id
FROM
Countries C
WHERE
LEFT(customers.Phone, LEN(C.Prefix)) = C.Prefix AND
LEN(customers.phone) >= C.MinDigits

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[UpdateCustomer]
AS
BEGIN
SELECT *
INTO #CustomerTable
FROM dbo.customers
DECLARE #Phone NVARCHAR(MAX)
DECLARE #CountryID INT
DECLARE #Count INT
DECLARE #CustomerID INT
WHILE EXISTS (SELECT * FROM #CustomerTable)
BEGIN
SELECT TOP 1
#Phone = Phone, #CustomerID = Id
FROM
#CustomerTable
IF((SELECT TOP 1 COUNT(*)
FROM countries
WHERE #Phone LIKE Prefix+'%' And LEN(#Phone) >= MinDigits) > 0)
BEGIN
SELECT TOP 1
#CountryID = Id
FROM
countries
WHERE
#Phone LIKE Prefix + '%'
AND LEN(#Phone) >= MinDigits
UPDATE customers
SET CountryID = #CountryID
WHERE Id = #CustomerID
END
DELETE #CustomerTable
WHERE Id = #CustomerID
END
DROP TABLE #CustomerTable
END

Related

How to insert data into a temporary table using an existing table and new columns

I am trying to insert data into a temporary table within my stored procedure. The data is selected from an existing table and creating new columns with concatenated data. I'm getting an error that the column name or number of supplied values does not match table definition. I'm pretty certain that the code in my application is correct so I believe the issue is with the way I'm storing the data in a temporary table.
Here is my proc:
AS
BEGIN
CREATE TABLE #TempTable
(
[ID] [varchar](10),
[FIRST_NAME] varchar(50),
[LAST_NAME] varchar(50),
[WEBSITE_LINK] varchar(200)
)
INSERT INTO #TempTable
SELECT USER.ID,USER.FIRSTNAME AS [FIRST_NAME], USER.LASTNAME AS
[LAST_NAME]
FROM USER
WHERE USER.Registered = 'Yes'
DECLARE #Link1 NVARCHAR(100)
DECLARE #Link2 VARCHAR(10)
DECLARE #Link3 NVARCHAR(4)
SET #Link1 = 'http://www.mywebsite.com/user/'
SET #Link2 = (SELECT USER.ID FROM USER WHERE USER.Registered =
'Yes')
SET #Link3 ='/document.doc'
SET #WEBSITE_LINK = (SELECT concat(#Link1,#Link2,#Link3 )AS
[WEBSITE_LINK])
DROP TABLE #TempTable
END
I think this is your problem:
SET #Link2 = (SELECT USER.ID FROM USER WHERE USER.Registered = 'Yes')
What if there are six of them? A single variable can't hold all of them. You can do:
SELECT TOP(1) #Link2 = USER.ID FROM USER WHERE USER.Registered = 'Yes' ORDER BY [SOMETHING];
If the goal is to create a temp table with a full [WEBSITE_LINK], you can do that without all those variables:
BEGIN
CREATE TABLE #TempTable
(
[ID] [varchar](10),
[FIRST_NAME] varchar(50),
[LAST_NAME] varchar(50),
[WEBSITE_LINK] varchar(200)
)
INSERT INTO #TempTable
SELECT DISTINCT u.ID
, [FIRST_NAME] = u.FIRSTNAME
, [LAST_NAME] = u.LASTNAME
, [WEBSITE_LINK] = 'http://www.mywebsite.com/user/' +
CAST(u.ID AS VARCHAR(10)) +
'/document.doc'
FROM [USER] u
WHERE u.Registered = 'Yes'
-- Do something with these values...
DROP TABLE #TempTable
END

MSSQL - How to not allow multiple users to view same set of records

e.g.
in table I have following records
a
b
c
d
e
f
g
h
I want to retrieved records by page
say 4 records per page
user x picks
a
b
c
d
Now user y should not pick any of the above
e
f
g
h
user x processes a record say record b
now he should see
a
e
c
d
and user y should see
f
g
h
i
how can I accomplish this, is there any built in way in mssql?
UPDATE
Here's what I have accomplished so far using auxilary table
http://sqlfiddle.com/#!18/e96f1/1
AllocateRecords2 2, 5, 1
GO
AllocateRecords2 2, 5, 2
both the queries are returning same set of results
I think you can use an auxiliary table UserData
CREATE TABLE Data(
ID int NOT NULL IDENTITY PRIMARY KEY,
Value varchar(1) NOT NULL
)
INSERT Data(Value)VALUES
('a'),
('b'),
('c'),
('d'),
('e'),
('f'),
('g'),
('h')
-- auxiliary table
CREATE TABLE UserData(
UserID int NOT NULL,
DataID int NOT NULL,
PRIMARY KEY(UserID,DataID),
FOREIGN KEY(DataID) REFERENCES Data(ID)
)
GO
And fill this table using the following procedure
CREATE PROC AddDataToUserData
#UserID int
AS
INSERT UserData(DataID,UserID)
SELECT TOP 4 ID,#UserID
FROM Data d
WHERE ID NOT IN(SELECT DataID FROM UserData)
ORDER BY ID
GO
Execute procedure for each other users
--TRUNCATE TABLE UserData
EXEC AddDataToUserData 1
EXEC AddDataToUserData 2
EXEC AddDataToUserData 3
...
Select data for a specific user
SELECT d.*
FROM Data d
JOIN UserData u ON u.DataID=d.ID
WHERE u.UserID=1
SELECT d.*
FROM Data d
JOIN UserData u ON u.DataID=d.ID
WHERE u.UserID=2
You can also create procedure for it
CREATE PROC GetDataForUser
#UserID int
AS
SELECT d.*
FROM Data d
JOIN UserData u ON u.DataID=d.ID
WHERE u.UserID=#UserID
GO
And then use it
EXEC GetDataForUser 1
EXEC GetDataForUser 2
Hope I understood your question correctly. But if I'm wrong you may use it as an idea.
I've added one column PageNumber into AllocatedRecords
/*
DROP TABLE Alphabets
DROP TABLE AllocatedRecords
GO
*/
CREATE TABLE Alphabets
(
ID INT IDENTITY(1,1) PRIMARY KEY,
Record varchar(1)
)
GO
CREATE TABLE [dbo].[AllocatedRecords](
[ID] [bigint] IDENTITY(1,1) NOT NULL primary key,
[ReferenceID] [int] NULL,
[IsProcessed] [bit] NULL,
[AllocatedToUser] [int] NULL,
[AllocatedDate] [datetime] NULL,
[ProcessedDate] [datetime] NULL,
PageNumber int -- new column
)
GO
INSERT Alphabets VALUES('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')
GO
And changed your procedure
DROP PROC AllocateRecords2
GO
CREATE PROC AllocateRecords2
(
#UserID INT,
#PageSize INT,
#PageNumber INT
)
AS
BEGIN
DECLARE #Today DATETIME
SET #Today = GETDATE()
--deallocated expired items
--TRUNCATE TABLE AllocatedRecords
DELETE AllocatedRecords
WHERE IsProcessed = 0 AND
(
(DATEDIFF(minute, #Today, AllocatedDate) > 5)
OR (AllocatedToUser = #UserID AND PageNumber <> #PageNumber)
)
DECLARE
#Draw INT = 10,
#PoolSize INT = #PageSize,
#CurrentRecords INT = (SELECT Count(*) from AllocatedRecords WHERE AllocatedToUser = #UserID AND IsProcessed = 0)
IF #CurrentRecords = 0
BEGIN
SET #Draw = #PoolSize
END
ELSE IF #CurrentRecords < #PoolSize
BEGIN
SET #Draw = #PoolSize - #CurrentRecords
END
ELSE IF #CurrentRecords >= #PoolSize
BEGIN
SET #Draw = 0
END
IF #Draw>0
BEGIN
INSERT AllocatedRecords(ReferenceID,IsProcessed,AllocatedToUser,AllocatedDate,ProcessedDate,PageNumber)
SELECT ID, 0, #UserID, GETDATE(), NULL, #PageNumber
FROM Alphabets
WHERE ID NOT IN (SELECT ReferenceID FROM AllocatedRecords)
ORDER BY ID
OFFSET (#PageNumber - 1) * #PageSize ROWS
FETCH NEXT #Draw ROWS ONLY
END
SELECT x.ID, x.Record
FROM AllocatedRecords A
JOIN Alphabets x ON A.ReferenceID = x.ID
WHERE AllocatedToUser = #UserID
AND IsProcessed = 0
SELECT COUNT(*) as TotalRecords
FROM AllocatedRecords
WHERE AllocatedToUser = #UserID
AND IsProcessed = 0
END
GO
Test
TRUNCATE TABLE AllocatedRecords
GO
-- user 2
EXEC AllocateRecords2 2, 5, 1
EXEC AllocateRecords2 2, 5, 2
EXEC AllocateRecords2 2, 5, 2 -- the same page
EXEC AllocateRecords2 1, 5, 1 -- user 1
EXEC AllocateRecords2 3, 5, 1 -- user 3

Query to add up multiple names from TVP

I have a table of name value pairs like this
Properties
- EmployeeId
My table for employee looks like this:-
EmployeeId FirstName Middle LastName
1 John Smith
2 Rick Steve James
3 Maddy Y Angela
Now my TVP can contain multiple or single EmployeeId's
I want to add up the full names of all the person in TVP in a single parameter.
i.e.
For eg. If I receive 1 and 3 in TVP
My output should be John Smith, Maddy Y Angela.
Do I need to use cursors in this?
What is the best way to accomplish this?
EDIT
The Scipt For the TVP is as below:-
CREATE TYPE [dbo].[Employee] AS TABLE( [EmployeeId] [uniqueidentifier] NULL)GO
The desired output is the Full Names of Employees in the TVP comma seperated.
Assuming you have a table type like:
CREATE TYPE dbo.TVPTypeName AS TABLE(EmployeeId INT PRIMARY KEY);
Then you can just perform a join:
CREATE PROCEDURE dbo.foo
#bar dbo.TVPTypeName READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT STUFF((SELECT ','
+ e.FirstName
+ COALESCE(' ' + NULLIF(RTRIM(e.Middle),''), ' ')
+ e.LastName
FROM dbo.Employees AS e
INNER JOIN #bar AS b
ON e.EmployeeId = b.EmployeeId
FOR XML PATH(''),TYPE).value('.[1]','nvarchar(max)'),1,1,'');
END
GO
CREATE TYPE dbo.IntArray AS TABLE(IntValue INT PRIMARY KEY);
GO
CREATE TABLE dbo.Employee(
EmployeeID INT IDENTITY(1,1) PRIMARY KEY,
FirstName NVARCHAR(50) NOT NULL,
MiddleName NVARCHAR(50) NULL,
LastName NVARCHAR(50) NOT NULL
);
GO
INSERT INTO dbo.Employee VALUES ('John',NULL,'Smith');
INSERT INTO dbo.Employee VALUES ('Rick','Steve','James');
INSERT INTO dbo.Employee VALUES ('Maddy','Y','Angela');
GO
CREATE PROCEDURE dbo.CocoJambo(#pEmployee dbo.IntArray READONLY)
AS
BEGIN
DECLARE #Result NVARCHAR(MAX);
SELECT #Result=ISNULL(#Result,'')+','+FirstName+ISNULL(' '+MiddleName,'')+' '+LastName
FROM dbo.Employee e INNER JOIN #pEmployee p ON e.EmployeeID=p.IntValue;
SELECT STUFF(#Result,1,1,'') AS [Result];
END;
GO
-- Test
DECLARE #p dbo.IntArray;
INSERT INTO #p VALUES (1);
INSERT INTO #p VALUES (3);
EXEC dbo.CocoJambo #pEmployee=#p;
/*
Result
-------------------------
John Smith,Maddy Y Angela
*/

SQL Server : stored procedure or function

I have a table with 2 columns (A as bool and B as text), these columns can be:
both are null
if A is False, then B should be null
if A is True, then B should be not null
There are rules. I want to create a stored procedure or function to check these rules when row adding or updating (via trigger). What is better, stored procedure or function? If function, which type? In general, which variant is the best (return boolean or other way etc)?
I think you're after a CHECK Constraint.
Example:
ALTER TABLE Xxx
ADD CONSTRAINT chk_Xxx
CHECK ( (A IS NULL AND B IS NULL)
OR (A = 0 AND B IS NULL)
OR (A = 1 AND B IS NOT NULL)
) ;
I would use a CHECK CONSTRAINT wired up to a UDF.
Here is a silly example verifying that if you insert a Person, their age will be greater than 17.
if NOT exists (select * from sysobjects
where id = object_id('dbo.udfOlderThan17Check') and sysstat & 0xf = 0)
BEGIN
print 'Creating the stubbed version of dbo.udfOlderThan17Check'
EXEC ( 'CREATE FUNCTION dbo.udfOlderThan17Check ( #j as smallint ) RETURNS bit AS BEGIN RETURN 0 END')
END
GO
ALTER FUNCTION dbo.udfOlderThan17Check ( #Age smallint )
RETURNS bit AS
BEGIN
declare #exists int
select #exists = 0
if ( #Age IS NULL )
BEGIN
select #exists = 1 -- NULL VALUES SHOULD NOT BLOW UP THE CONSTRAINT CHECK
END
if ( #exists = 0 )
BEGIN
if #Age > 17
begin
select #exists = 1
end
END
return #exists
END
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[Person]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
DROP TABLE [dbo].[Person]
END
GO
CREATE TABLE [dbo].[Person]
(
PersonUUID [UNIQUEIDENTIFIER] NOT NULL DEFAULT NEWSEQUENTIALID()
, Age smallint not null
)
GO
ALTER TABLE dbo.Person ADD CONSTRAINT PK_Person
PRIMARY KEY NONCLUSTERED (PersonUUID)
GO
ALTER TABLE dbo.Person
ADD CONSTRAINT [CK_Person_AgeValue] CHECK ([dbo].[udfOlderThan17Check]( [Age] ) != 0)
GO
Here are some "tests":
INSERT INTO dbo.Person (Age) values (33)
INSERT INTO dbo.Person (Age) values (16)
INSERT INTO dbo.Person (Age) select 333 UNION select 58
INSERT INTO dbo.Person (Age) select 444 UNION select 4
select * from dbo.Person

write a Query Unique ids with different city's

In A Table Cityid and CITYname 2 Columns:
city id city name
1 Bang
1 hyd
1 pune
2 hyd
2 pune
2 chennai
I want the Result:
1 ---hyd,pune,bang(all citynames of city id)
2---
You left off some key details required to answer your question: What language are you using?
Here is the answer if you are using T-SQL on SQL Server 2005/2008:
Your Table:
CREATE TABLE [dbo].[cities](
[cityid] [int] NULL,
[cityname] [varchar](50) NULL
) ON [PRIMARY]
Your Data:
insert into cities(cityid,cityname)values(1,'Seattle')
insert into cities(cityid,cityname)values(1,'Portland')
insert into cities(cityid,cityname)values(2,'New York')
insert into cities(cityid,cityname)values(2,'Newark')
Your Query:
declare #result table(
cityid int,
cityname varchar(max)
)
declare #queue table(
cityid int,
cityname varchar(50)
)
declare #cityid int
declare #cityname varchar(50)
insert into #queue select * from cities
while(exists(select top 1 cityid from #queue)) begin
select top 1 #cityid = cityid, #cityname = cityname from #queue
if(exists(select cityid from #result where cityid = #cityid)) begin
update #result
set cityname = cityname + ', ' + #cityname
where cityid = #cityid
end else begin
insert into #result(cityid,cityname) values(#cityid,#cityname)
end
delete from #queue where cityid = #cityid and cityname = #cityname
end
select * from #result
Your Result:
cityid cityname
1 1 Seattle, Portland
2 2 New York, Newark
look up GROUP_CONCAT
or USER DEFINED AGGREGATE FUNCTIONS

Resources