Entity Framework stored procedure handling unassigned values - sql-server

I have a stored procedure which returns user Token if authentication passes like this
BEGIN
SET FMTONLY OFF --Tricky Part
DECLARE #token uniqueidentifier
DECLARE #user_id as int
SET #user_id = (SELECT UserID FROM Users
WHERE #email = Email AND #password = PasswordKey)
IF #user_id IS NOT NULL
BEGIN
SET #token = NEWID()
UPDATE Users SET Token = #token
WHERE UserID = #user_id
SELECT * FROM Users WHERE UserID = #user_id
END
END
Without SET FMTONLY OFF it returns Token BUT only if user entered correct cardinalities else error
A member of the type, 'Token', does not have a corresponding column in the data reader with the same name.
occurs.
Now I have another stored procedure (almost same as this one) which returns single Product determined by ID which I pass to the stored procedure and it works fine even when I send non-existing ones. In function import, one stored procedure shows me columns which returns and another one doesn't. For clarity here are two images which shows stored procedures and function import Images

Instead of using:
SELECT * FROM Users WHERE UserID = #user_id
List out the names of the columns that you wish to return from the Users table instead of just using SELECT *.
SELECT ColumnName,
AnotherColumn,
YouGetThePoint
FROM ...
It sounds like the DataReader is being populated with column names that are different than the expected column names. Have you stepped through with a debugger and/or listed out the actual column names that are being returned from the procedures to is if they are just being given arbitrary names (e.g. T1, T2).

Related

SQL stored procedure , applying where clause

1) In SQL Server I have stored procedure and it has joins with multiple tables.
2) In that, UserMaster, RoleMaster and UserType table are there
3) Role Master will have the Following roles RoleX, RoleY and RoleZ
4) In User type master we have user type values like Type1 and Type2 only for RoleZ (As per requirement)
5) Now in the user table I have n number of users, and they are associated with any one of the above roles and only for RoleZ they may associated with either one of the user type
Problem:
Now in the GUI I will get user filter with the Role and user type, and they will send more than one role for filter , if they apply filter for RoleZ then only they will give the usertype filter, how can I write a query to fetch the record for this scenario
Kindly help on this quickly
The parameters (filter conditions) must be variables and the use of dynamic query can be helpful.
For one parameter (filter condition) below is the example.
if(#param1 is not null)
then
begin
declare #var varchar(max)= 'select * from tablename where columnname = ' + #param1
exec(#var)
end
if(#param1 is not null and #param2 is not null)
then
begin
declare #var varchar(max)= 'select * from tablename where columnname1 = ' + #param1 + 'and columnname2 = ' + #param2
exec(#var)
end
necessary casting should be done based on your requirements and the assignment of #var variables should change depending on the parameters dynamically.
You can check If.. Else to keep it simple in the stored procedure.
If(#usertype is not null)
Begin
//query for user type
Else
//query for role only
End
This can get you started
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql

How to Update or Insert a record using SQL Merge

I have a windows forms application that needs to edit an existing record if it already exists and create it if it does not. I'm using SQL Server 2008 R2. My application reads data from various tables which includes an ID field for the output table if a record already exists.
The ID field is blank if a new record is being created. The ID field is the primary key and an Identity (auto increment) field for the destination table.
I have created a stored procedure using MERGE that I hope will create a new record or update the existing one. The update part is working but I can't figure out what to do with the ID field when creating.
When doing an update I pass in an ID Parameter and the existing record is located. Obviously if it is a new record I won't have an ID yet but I can't then leave that Parameter out or I get an unassigned variable error as you would expect.
Here is my stored procedure. Am I just barking up the wrong tree here
somewhere?
Should I just create two stored procedures and call Update if I have and ID and Call Create if I don't have and ID?
Thanks for any assistance.
USE [Insurance]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[CreateModifyValuation]
-- Add the parameters for the stored procedure here
#ValuationID int,
#OwnersCorporationID int,
#ValDate datetime,
#ValuerID int,
#Amount money,
#Printed bit,
#Approved bit,
#Notes varchar(max),
#MultiplierValue money,
#MultiplierClass char(10),
#Adjustment money,
#SubmittedDate datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
Merge Valuation as Target
USING (Select
#ValuationID,
#OwnersCorporationID,
#ValDate,
#ValuerID,
#Amount,
#Printed,
#Approved,
#Notes,
#MultiplierValue,
#MultiplierClass,
#Adjustment,
#SubmittedDate
)
As Source(
ValuationID,
OwnersCorporationID,
ValDate,
ValuerID,
Amount,
Printed,
Approved,
Notes,
MultiplierValue,
MultiplierClass,
Adjustment,
SubmittedDate
)
ON Source.ValuationID = Target.ValuationID
WHEN MATCHED THEN
UPDATE SET
Target.OwnersCorporationID = Source.OwnersCorporationID,
Target.ValDate = Source.ValDate,
Target.ValuerID = Source.ValuerID,
Target.Amount = Source.Amount,
Target.Printed = Source.Printed,
Target.Approved = Source.Approved,
Target.Notes = Source.Notes,
Target.MultiplierValue = Source.MultiplierValue,
Target.MultiplierClass = Source.MultiplierClass,
Target.Adjustment = Source.Adjustment,
Target.SubmittedDate = Source.SubmittedDate
WHEN NOT MATCHED BY Target THEN
INSERT (
OwnerscorporationID,
ValDate,
ValuerID,
Amount,
Printed,
Approved,
Notes,
MultiplierValue,
MultiplierClass,
Adjustment,
SubmittedDate
)
VALUES (
Source.OwnersCorporationID,
Source.ValDate,
Source.ValuerID,
Source.Amount,
Source.Printed,
Source.Approved,
Source.Notes,
Source.MultiplierValue,
Source.MultiplierClass,
Source.Adjustment,
Source.SubmittedDate
);
END
I feel like I cheated but it's only one line so how bad can it be :)
In My SQL I added this line before the "SET NOCOUNT ON;"
(Funny if I put it after SET NOCOUNT ON I get a syntax error)
if (#ValuationID = 0) set #ValuationID = null
Then in my C# code I set the ID to 0 for a new record and it seems to work after a couple of tests. There may be a better way to do this but like most things in life you stop looking once it works.
Thanks again to those who commented.
David

Generating SSRS report in different location passed on the parameter passed

I have a SSRS report which accepts a parameter (Country_Name).
This is a weekly report and needs to be generated automatically by triggering the SQL agent job. So, I have created a data driven subscription for this report and created a SSIS package to generate the report from SQL agent job.
Now the problem is : Since it is a automated report, there is no interface to pass the parameter to report. So, I have created a table to hold the list of parameters to be passed and the path to place the generated report.
Table will have 2 columns (Country_Name & Report_Path) and hold values like (India \AB123C\India) (China,\ABC\China) etc.
Depending on the parameter passed, location of the report will change. So, I used looping in data driven subscription query to get the parameter and path. Though there are multiple parameters and path for that respective parameter are returned by the query, it is picking only the first result set and generates report only for the first parameter and its specified location.
I unable to find out the solution for this. Please find the below query I have used for your reference. It would be great if I can get a solution for this.
DECLARE #MinCount INT = 1
DECLARE #Country VARCHAR(100)
DECLARE #Path VARCHAR(MAX)
DECLARE #RecordCount INT
DECLARE #CurrentDate VARCHAR(10)
DECLARE #CountryList Table (ID INT Identity(1,1),Country_Name Varchar(100),Report_path Varchar(max))
SET #RecordCount = (SELECT COUNT(*) FROM Country_List)
WHILE (#MinCOunt < = #RecordCount)
BEGIN
SET #Country = (Select Country_name from Country_List WHere ID = #MinCOunt)
SET #Path = (Select Report_Path from Country_List WHere ID = #MinCOunt)
SET #CurrentDate = ( SELECT CONVERT(char(10), GetDate(),126) as currentSysDate)
Delete from #CountryList
insert into #CountryList (Country_Name,Report_path) Values (#Country,#Path)
select 'Country_Details ' +
#CurrentDate as filename
,'Excel' as RenderFormat
, (select Name from Master where
Reference_Name = 'USER_NAME'
) as sqlUserId
,( select Value from Master where
Reference_Name = 'PASSWORD'
) as sqlPwd , (Select Country_Name from #CountryList) AS Issue_Country,
(Select Report_path from #CountryList) AS filePath_cfonereports
SET #MinCOunt = #MinCOunt + 1
END
I have resolved this issue now by using a different approach rather than using looping in subscription code. I created a reference table to store to the list of parameters as well as the corresponding path. Since this report is called from a SSIS package, I have used a variable to get the list of parameters from the reference table and a foreach loop container with Execute SQL task to pass one parameter from variable at a time and invoke the subscription. So that the report will be generated for that parameter passed and in the path specified. This foreach loop container will loop n times based on the count of parameters in the variable

how to update sql column(s) when column-names are not known in advance WITHOUT dynamic sql

SQl Server 2008
example:
on the page to edit user profile settings like DOB, gender, name etc..., I wouldn't know which columns user wanted me to update. Should I pass in all these values to the stored procedure, and update all of the profile columns values even though the user may have changed only one or two of them? It seems a little bad, is it?
Using dynamic sql is not an option due to security concerns.
You could employ a pattern where you have all of your fields be parameters to the stored procedure and have the default values for the parameters be sentinel values outside the domain for the fields (respectively). Then you write an update statement like:
update table
set
age = case when #age = -1 then age else #age end,
weight = case when #weight = -1 then weight else #weight end,
...
You could accomplish something similar with the fields as parameters and one additional bitfield parameter which would specify whether a given column was updated or not. That would go something like this:
update table
set
age = case when #updated & 0x01 then #age else age end,
weight = case when #updated = 0x02 then #weight else weight end,
...
They both have their pros and cons.
Create a stored procedure that has default parameters set to null. Then just supply the parameters that you want to change. In the update statement, check for null values like this Age = isnull(#Age, Age).
See the example below:
-- sample table
create table UserProfile
(
ID int identity(1,1),
UserName nvarchar(10),
Gender char(1),
Age int
)
insert into UserProfile values('Bob', 'M', 19)
insert into UserProfile values('June', 'F', 23)
-- stored proc
create proc UpdateUser
(
#ID int,
#UserName nvarchar(10) = null,
#Gender char(1) = null,
#Age int = null
)
as
begin
update UserProfile set
UserName = isnull(#UserName, UserName),
Gender = isnull(#Gender, Gender),
Age = isnull(#Age, Age)
where ID = #ID
end
-- usage:
exec UpdateUser #ID=1, #Age=29
exec UpdateUser #ID=1, #UserName='Bill'
exec UpdateUser #ID=2, #UserName='Julie', #Age=24
exec UpdateUser #ID=2, #UserName='Mitch', #Gender='M', #Age=56
select * from UserProfile
There's no way to update tables based on the column number of anything like that. You would have to use dynamic SQL to do this. If done correctly with parametrized queries, loginless users, execute as and sp_executesql the risks can removed.
I don't really see that it matters. If you need to write the stored proc to update any one column, then it shouldn't really matter if you are updating one, or all columns. I'd be surprised if you faced major performance implications by writing several columns of information rather than just one.
If you are concerned about updating data unnecessarily (ie. when nothing has really changed but the user clicked save anyway), and potentially messing with audit triggers etc then look at ways to detect that the user actually modified a value on your form compared to what was originally read from the database by marking the data as dirty

SQL Server 2008 Try/Catch parameter names and values

I have searched, but was not able to find how to get the parameters names and values of a stored procedure from the catch section of a try/catch in SQL server 2008.
I know how to get the parameters for example:
SELECT parm.name AS Parameter
,typ.name AS [Type]
FROM sys.procedures sp
JOIN sys.parameters parm ON sp.object_id = parm.object_id
JOIN sys.types typ ON parm.system_type_id = typ.system_type_id
WHERE sp.name = OBJECT_NAME(##procid)
Basically what I am looking for is the SP that executed with parameters and values so I can log and troubleshoot. Something like:
wsp_GetUser_s #UserID = 123, #UserName = 'juser'
There is no feature in Microsoft SQL Server's catch block to automatically log what parameters were passed in to the database object.
If you want to know what parameters are passed in, I recommend running a trace or using Extended Events.
If neither of those options are feasible I recommend creating a table to store this information. One column can have the Stored Procedure name and another column can have the parameters. The parameters column can contain a comma delimited list of whatever parameters were passed into the stored procedure.
Example:
create table dbo.Parameters (
RowID int identity(1,1),
StoredProcedure varchar(255),
Parameters varchar(max)
)
create proc dbo.wsp_GetUser_s (
#UserID int,
#UserName varchar(255)
)
as
begin try
select *
from dbo.Users
where UserID = #UserID
and UserName = #UserName
-- We'll only log to the Parameters table if we don't find a match
if ##rowcount < 1
begin
RAISERROR ('Did not find user. Logging passed parameters to dbo.Parameters table', -- Message text.
16, -- Severity.
1 -- State.
);
end
end try
begin catch
-- This could be moved into the catch block if you wanted to log this for all calls and not only when there is no match found for the passed in parameters.
insert into dbo.Parameters ( StoredProcedure, Parameters)
select 'wsp_GetUser_s', '#UserID=' + #UserID + ', #UserName=' + #UserName
end catch

Resources