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
Related
I have table LessonHour with empty Number column.
TABLE [dbo].[LessonHour]
(
[Id] [uniqueidentifier] NOT NULL,
[StartTime] [time](7) NOT NULL,
[EndTime] [time](7) NOT NULL,
[SchoolId] [uniqueidentifier] NOT NULL,
[Number] [int] NULL
)
How can I fill up the table with Number for each LessonHour so it would be the number of lesson hour in order?
The LessonHours cannot cross each other. Every school has defined its own lesson hour schema.
Example set of data
http://pastebin.com/efWCtUbv
What'd I do:
Order by SchoolId and StartTime
Use Cursor to insert into row next number, starting from 1 every time the SchoolId changes.
Edit:
Solution with cursor
select -- top 20
LH.[Id],
[StartTime],
[EndTime],
[SchoolId]
into #LH
from
LessonHour as LH
join RowStatus as RS on LH.RowStatusId = RS.Id
where
RS.IsActive = 1
select * from #LH order by SchoolId, StartTime
declare #id uniqueidentifier, #st time(7), #et time(7), #sid uniqueidentifier
declare #prev_sid uniqueidentifier = NEWID()
declare #i int = 1
declare cur scroll cursor for
select * from #LH order by SchoolId, StartTime
open cur;
fetch next from cur into #id, #st, #et, #sid
while ##FETCH_STATUS = 0
begin
--print #prev_sid
if #sid <> #prev_sid
begin
set #i = 1
end
update LessonHour set Number = #i where Id = #id
print #i
set #i = #i + 1
set #prev_sid = #sid
fetch next from cur into #id, #st, #et, #sid
end;
close cur;
deallocate cur;
drop table #LH
This is the result I was after http://pastebin.com/iZ8cnA6w
Merging the information from the StackOverflow questions SQL Update with row_number() and
How do I use ROW_NUMBER()?:
with cte as (
select number, ROW_NUMBER() OVER(partition by schoolid order by starttime asc) as r from lessonhour
)
update cte
set number = r
Would this work
CREATE TABLE [dbo].[LessonHour]
(
[Id] [uniqueidentifier] NOT NULL,
[StartTime] [time](7) NOT NULL,
[EndTime] [time](7) NOT NULL,
[SchoolId] [uniqueidentifier] NOT NULL,
[Number] AS DATEDIFF(hour,[StartTime],[EndTime])
)
So if I understand the question correctly you require a calculated column which takes in the values of [StartTime] and [EndTime] and returns the number of hours for that lesson as an int. The above table definition should do the trick.
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.
Is there a way to create UPDATE stored_procedure with parameters like:
#param1 int = null,
#param2 int = null,
#param3 nvarchar(255) = null,
#param4 bit = null,
#id int
and with UPDATE statement which will update only fields which are not NULL
so if I execute
spUpdateProcedure #param1=255, #id=1
if will update record #id=1 but it will change only field #param1 and will ignore changes to other #param2,3,4.
In other words, it wont change value for null in #param2,3,4
Thanks.
UPDATE YourTable
SET Column1 = COALESCE(#param1, Column1),
Column2 = COALESCE(#param2, Column2),
...
WHERE id = #id
on your edit statement, you can do this
update table
set
column1 = isnull(#param1,column1),
column2 isnull(#param2,column2)
I am trying to create a stored procedure that returns the ID of a user when the user firstname is entered correctly. Based on the returned ID I would like my IF condiction to return a unqiue number to tell me if user exists in database. I hope that makes sense. Thanks.
ALTER PROC dbo.PassParamUserID
#UserID int
AS
DECLARE #FirstName varchar(50)
set nocount on
SELECT f_Name
FROM tb_User
WHERE tb_User.f_Name = #FirstName;
BEGIN
IF #UserID is not null
RETURN 222
ELSE
RETURN 333;
SET NOCOUNT OFF;
END
If I understand you correctly, you are checking if the firstname exists in the table, and if it does, return the id for that firstname.. if it does not exist, then you want the procedure to return a code that tells you that the ID is missing
In that case, you want #FirstName coming in as a parameter and the #UserId variable gets selected from a matching row in the database
ALTER PROC dbo.PassParamUserID
#FirstName varchar(50)
AS
set nocount on
DECLARE #UserId INT
SELECT #UserID = UserId
FROM tb_User
WHERE tb_User.f_Name = #FirstName
IF #UserID IS NULL
BEGIN
SET #UserId = -999
END
SELECT #UserId
GO
It may be a better idea to check for matches involving both first and last name. Also, what do you do if the name exists multiple times? Use SET ROWCOUNT 1 to take care of this
ALTER PROC dbo.PassParamUserID
#FirstName varchar(50),
#LastName varchar(50)
AS
set nocount on
DECLARE #UserId INT
SET ROWCOUNT 1
SELECT #UserID = UserId
FROM tb_User
WHERE tb_User.f_Name = #FirstName
AND tb_User.l_Name = #LastName
SET ROWCOUNT 0
IF #UserID IS NULL
BEGIN
SET #UserId = -999
END
SELECT #UserId
GO
Using a bit out parameter
create procedure GetUserExists
#FirstName varchar(50),
#Ret bit out
as
if exists(select *
from tb_User
where tb_User.FirstName = #FirstName)
set #Ret = 1
else
set #Ret = 0
If I understood you correctly, this select query may work:
SELECT CASE WHEN EXISTS ( SELECT
TOP 1 1
FROM tb_User WHERE tb_User.f_Name = #FirstName ) THEN
1 ELSE 0 END
It will return a 1 if the user exists, 0 otherwise. It should perform more optimally because the use of top 1 (i.e. no need to scan for the existence of more than 1 row).
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