How to write more that one statement in the same SPROC - tsql

Since i don't have the ModuleID, I'd like to get from the Modules table first using the ModuleName.
ALTER PROCEDURE dbo.spEditTask
#ID int,
#ModuleName varchar(50),
#Task varchar(50),
#ListOfDevelopers varchar(50),
#StartDate date,
#PlannedEndDate date,
#EstimatedEndDate date,
#Status varchar(50),
#Comments varchar(500),
#LastAction varchar(50),
#Started bit
AS
BEGIN
DECLARE #ModuleID int;
SET #ModuleID =
(SELECT ModuleID
FROM Modules
WHERE ModuleName = #ModuleName);
GO
UPDATE DTasks
SET ModuleID = #ModuleID,
Task = #Task,
ListOfDevelopers = #ListOfDevelopers,
StartDate = #StartDate,
PlannedEndDate = #PlannedEndDate,
EstimatedEndDate = #EstimatedEndDate,
Status = #Status,
Comments = #Comments,
LastAction = #LastAction,
Started = #Started,
LastUpdated = GETDATE()
WHERE ID = #ID
GO
END
Thanks for helping

You need to wrap your lines in begin and end
ALTER PROCEDURE dbo.spEditTask
#ID int,
#ModuleName int,
#Task varchar(50)
as
begin
DECLARE #ModuleID int;
SELECT #ModuleID = ModuleID
FROM Modules
WHERE ModuleName = #ModuleName
UPDATE DTasks
SET ModuleID = #ModuleID,
Task = #Task,
WHERE ID = #ID
end
although you could use just a single line
UPDATE DTasks
SET
ModuleID = Modules.ModuleID,
Task = #task
FROM DTasks cross join Modules
WHERE DTasks.ID = #ID
AND Modules.ModuleName = #ModuleName

You are just missing the as keyword, and parentheses around the select expression:
ALTER PROCEDURE dbo.spEditTask
#ID int,
#ModuleName int,
#Task varchar(50)
AS
DECLARE #ModuleID int;
SET #ModuleID =
(SELECT ModuleID
FROM Modules
WHERE ModuleName = #ModuleName)
UPDATE DTasks
SET ModuleID = #ModuleID,
Task = #Task,
WHERE ID = #ID

Related

I cannot manage to insert multiple rows when having multiple fields to get data from

I ran into a problem with regards to trying to insert multiple rows in a table at once. I know it sounds easy, but here is the twist. The procedure itself gets data from a trigger, and the trigger returns a number of rows. So i need to make 1 insert statement to insert those rows and some other data. here is the code:
CREATE PROCEDURE [a01].[usp_raiseFriendAlerts]
(#AccountA UNIQUEIDENTIFIER, #AccountB UNIQUEIDENTIFIER)
AS
BEGIN
DECLARE #typeID TINYINT;
DECLARE #notificationID UNIQUEIDENTIFIER = NEWID();
DECLARE #accountAName NVARCHAR(356);
DECLARE #accountBName NVARCHAR(356);
SET #typeID = ( SELECT typeID
FROM [a01].[tbl_notificationTypes]
WHERE typeName = 'Added friend');
SET #accountAName = ( SELECT accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountA);
SET #accountBName = ( SELECT accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountB);
DECLARE #AccountIDZZ UNIQUEIDENTIFIER;
SET #AccountIDZZ = (SELECT friendAccountID
FROM [a01].[udf_getAddedFriendContacts](#AccountA, #AccountB)
EXCEPT
SELECT targetAccountID
FROM [a01].[tbl_blockedAccounts]);
INSERT INTO [a01].[tbl_notificationsInbox] (notificationID, notificationMessage, notificationDate, accountID, typeId)
VALUES (#notificationID, #accountAName + ' is now friends with ' + #accountBName, SYSDATETIMEOFFSET(), #AccountIDZZ , #typeID)
END;
GO
Try this:
CREATE PROCEDURE [a01].[usp_raiseFriendAlerts]
(
#AccountA UNIQUEIDENTIFIER
, #AccountB UNIQUEIDENTIFIER
)
AS
BEGIN
DECLARE #typeID TINYINT
, #notificationID UNIQUEIDENTIFIER = NEWID()
, #accountAName NVARCHAR(356)
, #accountBName NVARCHAR(356)
, #AccountIDZZ UNIQUEIDENTIFIER;
SELECT #typeID = typeID
FROM [a01].[tbl_notificationTypes]
WHERE typeName = 'Added friend';
SELECT #accountAName = accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountA;
SELECT #accountBName = accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountB;
INSERT INTO [a01].[tbl_notificationsInbox]
(
notificationID
, notificationMessage
, notificationDate
, accountID
, typeId
)
SELECT #notificationID
, #accountAName + ' is now friends with ' + #accountBName
, SYSDATETIMEOFFSET()
, AIDZZ.accountID
, #typeID
FROM (
SELECT friendAccountID AS 'accountID'
FROM [a01].[udf_getAddedFriendContacts](#AccountA, #AccountB)
EXCEPT
SELECT targetAccountID AS 'accountID'
FROM [a01].[tbl_blockedAccounts]
) AS AIDZZ
END;
GO

How to use openxml within a user defined function in SQL Server

I have an XML structure that I parse using OPENXML within a stored procedure to retrieve parameters used to perform a query. This procedure was a base procedure that a different stored procedure (procedure 2) is calling. Procedure 2 uses an insert-exec construct to get the data from the base procedure. This works great as long as we only call Procedure 2 or the base procedure.
My first problem is that I have a different procedure (procedure 3) that now needs to get the result from procedure 2 (I need the business rules that this procedure enforces), but cannot due to the message:
An INSERT EXEC statement cannot be nested.
I then tried to take the base procedure and make it a table valued function, but when I execute it, I receive the message:
Only functions and some extended stored procedures can be executed from within a function.
How do I get around one or both of these issues?
EDIT 1
I am including a code snippet to show the base procedure (Procedure 1) and the procedure implementing business requirements on the results of that procedure (Procedure 2). If there is a 3rd procedure that needs the results with the business rules applied, we run into problems.
create procedure dbo.p_Proc
#Xml xml
as
begin
set nocount on;
declare #l_idoc int
, #InfoId int
, #InfoTypeId int
, #Id int
, #Name varchar(50)
, #StatusId int
, #RoleId int
, #XmlBase xml
, #l_path varchar(100);
declare #T_TABLE table(
InfoId int
, InfoTypeId int
);
declare #T_RESULT table
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
EXEC sp_xml_preparedocument #l_idoc OUTPUT, #Xml;
set #l_path = '/xml/Info';
insert into #T_TABLE(InfoId, InfoTypeId)
select InfoId, InfoTypeId
from OPENXML (#l_idoc, #l_path, 1)
with (
InfoId int './#InfoId'
, InfoTypeId int './#InfoTypeId'
);
select #InfoId = InfoId
, #InfoTypeId = InfoTypeId
from #T_TABLE;
-- create the XML to call the base widgets
select #XmlBase =
(
select *
from
(
select t.Id, t.Name, t.StatusId, t.RoleId
from #T_TABLE w
inner join dbo.T_TABLE2 t
on t.InfoId = w.InfoId
and t.InfoTypeId = w.InfoTypeId
) b
for xml raw('Widget'), root('Xml')
);
-- retrieve widgets from base security
insert into #T_RESULT(Field1, Field2, Field3)
exec dbo.p_ProcBase #Xml = #XmlBase;
-- apply business logic here
select w.Field1, w.Field2, w.Field3
from #T_RESULT w;
end;
go
create procedure dbo.p_ProcBase
#Xml xml = null
as
begin
set nocount on;
declare #l_idoc int
, #Id int
, #Name varchar(50)
, #StatusId int
, #RoleId int
, #l_path varchar(100);
declare #T_Table table(
Id int
, Name varchar(50)
, StatusId int
, RoleId int
);
EXEC sp_xml_preparedocument #l_idoc OUTPUT, #Xml;
set #l_path = '/Xml/Widget';
insert into #T_Table(Id, Name, StatusId, RoleId)
select Id, Name, StatusId, RoleId
from OPENXML (#l_idoc, #l_path, 1)
with (
ProjectId int './#Id'
, WidgetTypeName varchar(50) './#Name'
, WorkflowStatusId int './#StatusId'
, UserRoleId bigint './#RoleId'
);
select #Id = w.Id
, #Name = w.Name
, #StatusId = w.StatusId
, #RoleId = w.RoleId
from #T_Table w;
-- retrieve enabled widgets for which the user has a role in the current workflow state
select t.Field1, t.Field2, t.Field3
from dbo.T_TABLE t
where t.StatusId = #StatusId
and t.RoleId = #RoleId;
end;
In order to send a data set (table) between procs, you must use a Table type, store the output of proc2 in a variable of table type and add a readonly only table type parameter to proc3
First you must create a table type to map your output from proc2:
CREATE TYPE T_RESULT AS TABLE
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
In dbo.p_Proc change #T_RESULT to:
declare #T_RESULT T_RESULT
Then create proc3:
CREATE PROCEDURE dbo.proc3
#T_RESULT T_RESULT READONLY
AS
BEGIN
SET NOCOUNT ON
INSERT INTO T3(...)
SELECT ... FROM #T_RESULT
END
Don't forget to add READONLY after a table type parameter in a proc.

Auto Increment of RateNumber

I have two tables:
1) BlogPost:
COLUMN_NAME DATA_TYPE
Id int
Title varchar
Description varchar
ImageName varchar
FileName varchar
CreatedDate datetime
Tags varchar
ModifiedDate datetime
RateNumber int
CreatedBy int
ShortDescription varchar
2. BlogRating:
Id EmployeeId PostId Rate
4 1 12 3
5 1 13 2
6 1 11 2
I wrote a stored procedure to Save the details of BlogRating:
ALTER PROCEDURE [dbo].[BlogRatingSave]
#EmployeeId int
,#PostId int
,#Rate int
AS
BEGIN
DELETE FROM [HRM_BlogRating]
WHERE [HRM_BlogRating].[EmployeeId] = #EmployeeId
AND [HRM_BlogRating].[PostId] = #PostId
INSERT INTO [HRM_BlogRating]
([EmployeeId]
,[PostId]
,[Rate]
)
VALUES
(#EmployeeId
,#PostId
,#Rate
)
END
What I want is that If different employees give rate, while saving BlogRating I need to auto increment the field RateNumber in table1by 1. At the same time if same employee rates next time there should not an increment for RateNumber. Please help me to solve this.
Why are you deleting and then inserting. Instead you should try to insert and if it fails (due to a duplicate) then do an update.
In the process you can also adjust the value of the rating, but I assume you will need to pass in the EmployeeId of the person making the change since that is part of your rule. ie.
update BlogPost set RateNumber = RateNumber + 1 where EmployeeId = #EmployeeID and CreatedBy != #EmployeeID
So it would all become:
ALTER PROCEDURE [dbo].[BlogRatingSave]
#EmployeeId int
,#PostId int
,#Rate int
AS
BEGIN
BEGIN TRY
INSERT INTO [HRM_BlogRating]
([EmployeeId]
,[PostId]
,[Rate]
)
VALUES
(#EmployeeId
,#PostId
,#Rate
)
END TRY
BEGIN CATCH
UPDATE [HRM_BlogRating]
SET Rate = #Rate
WHERE EmployeeID = #EmployeeID and PostId = #PostId
END CATCH
UPDATE BlogPost set RateNumber = RateNumber + 1 where EmployeeId = #EmployeeID and CreatedBy != #EmployeeID
END
Can I try this one as solution? #Michael
ALTER PROCEDURE [dbo].[BlogRatingSave]
#EmployeeId int
,#PostId int
,#Rate int
AS
BEGIN
IF(EXISTS(SELECT 1 FROM [HRM_BlogRating] WHERE EmployeeId= #EmployeeId AND [PostId] = #PostId))
BEGIN
DELETE
FROM
[HRM_BlogRating]
WHERE
[HRM_BlogRating].[EmployeeId]=#EmployeeId
AND
[HRM_BlogRating].[PostId] = #PostId
END
ELSE
BEGIN
UPDATE
HRM_BlogPost
SET
RateNumber=isnull(RateNumber,0)+1
WHERE
Id=#PostId
END
INSERT INTO [HRM_BlogRating]
([EmployeeId]
,[PostId]
,[Rate]
)
VALUES
(#EmployeeId
,#PostId
,#Rate
)
END

Case when exists then select

I have requirement to select the field from the table in case statement like instead of some static value.
WHEN EXISTS(SELECT c.customer_name FROM Sales.Customer AS c
WHERE c.PersonID = #BusinessEntityID)
THEN c.customer_name
How can this be achieved or is this possible . I have taken the following from msdn site. Need to tweak to fulfill my requirement.
USE AdventureWorks2008R2;
GO
CREATE FUNCTION dbo.GetContactInformation(#BusinessEntityID int)
RETURNS #retContactInformation TABLE
(
BusinessEntityID int NOT NULL,
FirstName nvarchar(50) NULL,
LastName nvarchar(50) NULL,
ContactType nvarchar(50) NULL,
PRIMARY KEY CLUSTERED (BusinessEntityID ASC)
)
AS
-- Returns the first name, last name and contact type for the specified contact.
BEGIN
DECLARE
#FirstName nvarchar(50),
#LastName nvarchar(50),
#ContactType nvarchar(50);
-- Get common contact information
SELECT
#BusinessEntityID = BusinessEntityID,
#FirstName = FirstName,
#LastName = LastName
FROM Person.Person
WHERE BusinessEntityID = #BusinessEntityID;
SET #ContactType =
CASE
-- Check for employee
WHEN EXISTS(SELECT * FROM HumanResources.Employee AS e
WHERE e.BusinessEntityID = #BusinessEntityID)
THEN 'Employee'
-- Check for vendor
WHEN EXISTS(SELECT * FROM Person.BusinessEntityContact AS bec
WHERE bec.BusinessEntityID = #BusinessEntityID)
THEN 'Vendor'
-- Check for store
WHEN EXISTS(SELECT * FROM Purchasing.Vendor AS v
WHERE v.BusinessEntityID = #BusinessEntityID)
THEN 'Store Contact'
-- Check for individual consumer
WHEN EXISTS(SELECT * FROM Sales.Customer AS c
WHERE c.PersonID = #BusinessEntityID)
THEN 'Consumer'
END;
-- Return the information to the caller
IF #BusinessEntityID IS NOT NULL
BEGIN
INSERT #retContactInformation
SELECT #BusinessEntityID, #FirstName, #LastName, #ContactType;
END;
RETURN;
END;
GO
No idea what the rest of your code looks like, but typically this would be:
SELECT name = COALESCE(c.customer_name, o.other_entity_name)
FROM dbo.MainEntity AS m
LEFT OUTER JOIN dbo.Customers AS c ON m.something = c.something
LEFT OUTER JOIN dbo.OtherTable AS o ON m.something = o.something;
But other than a general idea, you haven't given quite enough information to supply a complete answer.

sql missing rows when grouped by DAY, MONTH, YEAR

If I select from a table group by the month, day, year,
it only returns rows with records and leaves out combinations without any records, making it appear at a glance that every day or month has activity, you have to look at the date column actively for gaps. How can I get a row for every day/month/year, even when no data is present, in T-SQL?
Create a calendar table and outer join on that table
My developer got back to me with this code, underscores converted to dashes because StackOverflow was mangling underscores -- no numbers table required. Our example is complicated a bit by a join to another table, but maybe the code example will help someone someday.
declare #career-fair-id int
select #career-fair-id = 125
create table #data ([date] datetime null, [cumulative] int null)
declare #event-date datetime, #current-process-date datetime, #day-count int
select #event-date = (select careerfairdate from tbl-career-fair where careerfairid = #career-fair-id)
select #current-process-date = dateadd(day, -90, #event-date)
while #event-date <> #current-process-date
begin
select #current-process-date = dateadd(day, 1, #current-process-date)
select #day-count = (select count(*) from tbl-career-fair-junction where attendanceregister <= #current-process-date and careerfairid = #career-fair-id)
if #current-process-date <= getdate()
insert into #data ([date], [cumulative]) values(#current-process-date, #day-count)
end
select * from #data
drop table #data
Look into using a numbers table. While it can be hackish, it's the best method I've come by to quickly query missing data, or show all dates, or anything where you want to examine values within a range, regardless of whether all values in that range are used.
Building on what SQLMenace said, you can use a CROSS JOIN to quickly populate the table or efficiently create it in memory.
http://www.sitepoint.com/forums/showthread.php?t=562806
The task calls for a complete set of dates to be left-joined onto your data, such as
DECLARE #StartInt int
DECLARE #Increment int
DECLARE #Iterations int
SET #StartInt = 0
SET #Increment = 1
SET #Iterations = 365
SELECT
tCompleteDateSet.[Date]
,AggregatedMeasure = SUM(ISNULL(t.Data, 0))
FROM
(
SELECT
[Date] = dateadd(dd,GeneratedInt, #StartDate)
FROM
[dbo].[tvfUtilGenerateIntegerList] (
#StartInt,
,#Increment,
,#Iterations
)
) tCompleteDateSet
LEFT JOIN tblData t
ON (t.[Date] = tCompleteDateSet.[Date])
GROUP BY
tCompleteDateSet.[Date]
where the table-valued function tvfUtilGenerateIntegerList is defined as
-- Example Inputs
-- DECLARE #StartInt int
-- DECLARE #Increment int
-- DECLARE #Iterations int
-- SET #StartInt = 56200
-- SET #Increment = 1
-- SET #Iterations = 400
-- DECLARE #tblResults TABLE
-- (
-- IterationId int identity(1,1),
-- GeneratedInt int
-- )
-- =============================================
-- Author: 6eorge Jetson
-- Create date: 11/22/3333
-- Description: Generates and returns the desired list of integers as a table
-- =============================================
CREATE FUNCTION [dbo].[tvfUtilGenerateIntegerList]
(
#StartInt int,
#Increment int,
#Iterations int
)
RETURNS
#tblResults TABLE
(
IterationId int identity(1,1),
GeneratedInt int
)
AS
BEGIN
DECLARE #counter int
SET #counter= 0
WHILE (#counter < #Iterations)
BEGIN
INSERT #tblResults(GeneratedInt) VALUES(#StartInt + #counter*#Increment)
SET #counter = #counter + 1
END
RETURN
END
--Debug
--SELECT * FROM #tblResults