select statement blocking another select statement - sql

I'm not quite sure what is happening here. I have an alert that goes out if #StoreOrderID >1.
It goes like this:
declare #message varchar(1000)
#StoreID --(retrieves info from StoreOrders tables)
BEGIN
select #message = 'store' + storename
from StoreOrders as so
join Distributors as ds
on ds.DistributorID = so.StoreOrderID
where ds.SDistributorID = #StoreID
select #message = brandID
from StoreOrders a
join Brandtitles as b
on b.branddistributorID = a.StoreOrderID
where b.brandNum = #DistributorNum and b.branddistributorID = #StoreID
select #message = 'date' + ISNULL(convert(varchar, #Date),'')
select #message = 'cost' + ISNULL(Convert(varchar,#Cost),'')
Also for some reason if i try to concatenate a string unto the 'brand' select it throws an error. I can do it for the 'storename'. I think this may have something to do with it.
If I comment out the storename select it will send the alert for brandID, if I comment out the other one it does the other one. However, if I leave both of them, it will only show one of them. What am I doing wrong here?

As a user mentioned in the comments, you are overwriting the #message variable everytime you assign it a new value, you are not concatenating any of the values. A much simpler and cleaner way would be something like....
declare #message varchar(1000)
#StoreID [DataType]--(retrieves info from StoreOrders tables)
BEGIN
Declare #storename VARCHAR(100)
, #brandID VARCHAR(100)
, #Date VARCHAR(100)
, #Cost VARCHAR(100);
select #storename = 'store' + ISNULL(storename , 'Unknown')
from StoreOrders as so
join Distributors as ds
on ds.DistributorID = so.StoreOrderID
where ds.SDistributorID = #StoreID;
select #brandID = ISNULL(CAST( brandID AS VARCHAR(100) , 'Unknown')
from StoreOrders a
join Brandtitles as b
on b.branddistributorID = a.StoreOrderID
where b.brandNum = #DistributorNum
and b.branddistributorID = #StoreID;
select #Date = 'date' + ISNULL(convert(varchar, #Date),'');
select #Cost = 'cost' + ISNULL(Convert(varchar,#Cost),'');
SET #message = #storename + #brandID + #Date + #Cost;

I Assume the code "alerting" the message is after the snipped you showed.
Then, as you use the same variable #message as target for both assignments, you overwrite the contents you assigned the first time when doing the second assignment. Only the values from the second assignment make it to the "alerting" command.
Use two different variables or "alert" your message after each assignment.

Related

Executing select statement as variable from TVF

I have to get a list of results from a Table value function from a variable. I have done something like this:
DECLARE #Date char(8) = '20200508'
DECLARE #Type varchar(100) = 'Inbound'
DECLARE #Offset INT = 3600
DECLARE #EmployeeID INT = null
DECLARE #TypeFunc as varchar(max)
SET #TypeFunc= N'select EmpID, Callcount from dbo.fn_' + #Type + '('''+ #Date +''','+ CAST(#Offset as Varchar(100))+','+ CAST(#EmployeeID as varchar(100))+')';
EXEC (#TypeFunc)
I expect to see a list of results as if I'm doing a normal select query, however, it is just coming back with 'Commands completed successfully.' in the results grid, which doesn't seem like its doing it correctly.
The query it should run should look like
Select EmpID, Callcount From dbo.fn_Inbound('20200508', 3600, null)
Anything I'm missing here?
I found 2 mistakes in your Query:
1.) Use CONCAT instead of + because if any of your concatenating string is null it makes the whole Concatenation as NULL (For your case EmpID is null it will makes the Whole Query as null by using +)
2.)ISNULL(CAST(#EmployeeID as varchar(100)),'NULL') Use ISNULL fn to pass as null for that Parameter in your function
SET #TypeFunc= CONCAT(N'select EmpID, Callcount from dbo.fn_' , #Type , '(''', #Date
,''',', CAST(#Offset as Varchar(100)),',',ISNULL(CAST(#EmployeeID as
varchar(100)),'NULL'),')');

Avoid "copy&paste" method for stored procedure depending on parameter

I have 50 stored procedures that I need to add a new location to. Is there an alternative for writing my stored procedures in the following way? (where I copy the same select statement for each location)
IF #LOCATION = 'Canada'
BEGIN
SELECT location_id, location_description
INTO #tempAssetHistoryCANADA
FROM [SERVER20].[Shop_Canada].[dbo].[report_asset_history]
END
IF #LOCATION = 'USA'
BEGIN
SELECT location_id, location_description
INTO #tempAssetHistoryUSA
FROM [SERVER20].[Shop_USA].[dbo].[report_asset_history]
END
I have a select statement that fires if the #parameter = "x" And then the exact same select statement, but from a different data source with the same structure if #parameter = "y".
I'm wondering if there is a better way to write these stored procedures because in the future when I need to add a new location I will need to update all 50 stored procedures, and copy each statement and slightly alter it for the new locations data? I've researched around and haven't found anything helpful.
Thanks!
One possible way instead of using a dynamical query is to create a view:
CREATE VIEW dbo.Locations
AS
SELECT location_id, location_description, 'Canada' AS location
FROM [SERVER20].[Shop_Canada].[dbo].[report_asset_history]
UNION ALL
SELECT location_id, location_description, 'USA' AS location
FROM [SERVER20].[Shop_USA].[dbo].[report_asset_history]
And then using it:
SELECT location_id, location_description
INTO #tempAssetHistory
FROM [dbo].Locations
WHERE location = #LOCATION
If you have new tables [SERVER20].[Shop_XXX].[dbo].[report_asset_history] you will have to add them to your view.
Put the code that loads the temp table into table-valued function. Then call this function from all your other SPs that need the data
SELECT * INTO #TempAssetHistory FROM dbo.LoadTempAssetHistory(#Location)
:
: Use the data
:
The LoadTempAssetHistory SP would look something like (CODE NOT TESTED):
CREATE FUNCTION LoadTempAssetHistory
(
#LOCATION Varchar(50)
)
RETURNS TABLE
AS
RETURN
(
SELECT location_id, location_description
FROM [SERVER20].[Shop_Canada].[dbo].[report_asset_history]
WHERE #LOCATION='CANADA'
UNION
SELECT location_id, location_description
FROM [SERVER20].[Shop_USA].[dbo].[report_asset_history]
WHERE #LOCATION = 'USA'
)
Now you can amend the function when you have a new location (or decide to reorganise all your data) without needing to change 50 SPs.
First of all you don't need a different #temp table for each location. You're storing the same data columns in each. Secondly, you could store the "From" clause in a table based on location and then use dynamic sql to select into your temp table. I'll provide some code and example shortly.
DECLARE #fromClause VARCHAR(255)
DECLARE #sql VARCHAR(MAX)
--Explicitly create the temp table
CREATE TABLE #T (location_id int, location_description varchar(255) )
--Get the FROM clause
select #fromClause = fromClause from tblLocation WHERE location = #LOCATION
--Build the Dynamic SQL
SET #sql = 'SELECT location_id, location_description ' + #fromClause
--Insert into your temp table
INSERT INTO #T execute ( #sql )
--View the results
SELECT * FROM #T
Here is tblLocation definition
You could achieve this by having a #Temp table for all your assists and checking against a location table in your DB, then calling the variable when the SP is executed:
Call the SP:
SP_Some_SP 'Canada'
Inside the SP
Declare #Location Varchar(100)
Declare #Location_ID int = (Select Location_ID from [Location] where Location_Description = #Location)
CREATE TABLE #TempAssetHistory
(
location_ID int,
location_Description varchar(100)
)
If Exists(Select Location_Description from [Location] where Location_Description = #Location )
BEGIN
Insert INTO #TempAssetHistory
Values(#Location_ID,#Location)
END
ELSE
BEGIN
-- Do something
END
You can use one table to store all selected data, use something like this:
DECLARE #country VARCHAR(20) = 'USA'
CREATE TABLE #tempHistory (country varchar(20), location_id int, location_description varchar(20))
DECLARE #sql VARCHAR(max)
SET #sql = 'SELECT ''' + #country + ''' as country, location_id, location_description FROM [SERVER20].[Shop_' + #country + '].[dbo].[report_asset_history]'
INSERT INTO #tempHistory EXEC (#sql)
Or you can use more flexible solution to put the list of countries as a parameter:
CREATE PROCEDURE dbo.prepare_tempHistory(#listOfCountries VARCHAR(max))
AS
DECLARE #sql varchar(max)
SET #sql = ''
SELECT #sql = #sql + ' UNION ALL ' +
'SELECT ''' + val + ''' as country, location_id, location_description FROM [SERVER20].[Shop_' + val + '].[dbo].[report_asset_history]'
FROM dbo.fnSplit(#listOfCountries, ',')
SET #sql = RIGHT(#sql, len(#sql)-11)
INSERT INTO #tempHistory EXEC (#sql)
GO
but then you need a small function to split parameters into table:
CREATE FUNCTION dbo.fnSplit
(
#delimited nvarchar(max),
#delimiter nvarchar(5)
)
RETURNS #ret TABLE (val nvarchar(max))
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #ret(val)
select r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
GO
Now you can prepare data on easy way:
CREATE TABLE #tempHistory (country varchar(20), location_id int, location_description varchar(20))
EXEC dbo.prepare_tempHistory 'USA,Canada,Mexico'
SELECT * FROM #tempHistory
for any additional country modify only the parameter
using suggestions from the answers and comments mentioning Dynamic SQL I came up with this little solution for myself. It's nicer then what Denis Rubashkin said because using his solution I would still have to copy the entire SQL query every time a new location is added. This way, I can just copy the 4 lines...
IF #LOCATION = 'CANADA'
BEGIN
SET #location_server = 'SHOP_Canada'
END
...from the IF statement at the beginning whenever I want to add a new location, not the whole SQL statement. It will replace the server name with the correct name in the parameter and append its name to the temp tables.
#LOCATION varchar(50),
#sqlCommand nvarchar(2000),
#location_server varchar(75)
IF #LOCATION = 'CANADA'
BEGIN
SET #location_server = 'SHOP_Canada'
END
IF #LOCATION = 'USA'
BEGIN
SET #location_server = 'SHOP_USA'
END
SET #sqlCommand = 'SELECT location_id, location_description
into ##MarineShopAssetExpensesLTD_'+#location_server+'
FROM [SERVER20].'+QUOTENAME(#location_server)+'.[dbo].[report_asset_history]
INNER JOIN [SERVER20].'+QUOTENAME(#location_server)+'.[dbo].imtbl_asset ON [report_asset_history].asset_id = imtbl_asset.id
GROUP BY location_id, location_description
EXECUTE sp_executesql #sqlCommand
As every SP is looking for different data, there is another, somewhat more drastic approach that you could use.
This approach requires that you put all the data SELECT statements for all 50 SPs into a single SP, say spDataLoad, which takes two parameters - a data set name and the location. The spDataLoad selects data based on which data set and which location are specified, and returns that requested data to the caller.
You will still need multiple select statements for each different combination of data set and location, but at least everything is in one single SP, and changes to the data will not affect all 50 SPs. If the tables and data for each location are the same, then you can subdivide the code into sections, one for each location, with identical code except for the database name corresponding to the location.
Using the code above as an example, if we choose 'AssetHistory' as the data set name then the code in your existing SPs would look like this:
:
:
CREATE TABLE #AssetHistory (
location_ID int,
location_Description varchar(100)
);
INSERT INTO #AssetHistory EXEC spDataLoad #DataSet='AssetHistory', #Location=#Location;
:
: use the data set
:
Now suppose you have another SP that requires the data set 'AssetDetails' then the code would be like this:
CREATE TABLE #AssetDetails (
:
: Specification for Asset Details table
:
);
INSERT INTO #AssetDetails EXEC spDataLoad #DataSet='AssetDetails', #Location=#Location;
:
: use the data set
:
The stored procedure spDataLoad, with multiple sections, for each of the locations, with separate selects based on the requested data set might look like this:
CREATE PROCEDURE spDataLoad
#DATASET varchar(20)
, #LOCATION Varchar(50)
AS
BEGIN
-- CANADA SECTION ------------------------------------
IF #LOCATION = 'CANADA'
BEGIN
IF #DATASET = 'AssetHistory'
SELECT location_id, location_description
FROM [SERVER20].[Shop_Canada].[dbo].[report_asset_history]
ELSE IF #DATASET = 'AssetDetails'
SELECT
: Asset details data
FROM [SERVER20].[Shop_Canada].[dbo].[report_asset_details]
ELSE IF #DATASET = '....'
:
: Etc, Etc for CANADA SECTION
END;
-- USA SECTION ------------------------------------
IF #LOCATION = 'USA'
BEGIN
IF #DATASET = 'AssetHistory'
SELECT location_id, location_description
FROM [SERVER20].[Shop_USA].[dbo].[report_asset_history]
ELSE IF #DATASET = 'AssetDetails'
SELECT
: Asset details data
FROM [SERVER20].[Shop_USA].[dbo].[report_asset_details]
ELSE IF #DATASET = '....'
:
: Etc, Etc for USA SECTION
END;
-- SOME OTHER SECTION ---------------------------
IF #LOCATION = 'SOME OTHER'
BEGIN
: Same logic
END
RETURN 0;
END
To manage performance you would probably need to add defaulted parameters for filtering which can be specified by the caller, and add WHERE clauses into the data set selections.

MSSQL says I am trying to convert a varchar to a string when I'm not

So I have this fairly long procedure at Work that I just made. What it does it not that important, but the end result is what matters.
I need to count some different types of descriptions in a table and that Works fine. I then need to take the two things that I Count and put them in a string that I return to my software. However, every time I run this procedure it gives me this:
Msg 245, Level 16, State 1, Procedure WorkDays, Line 43 Conversion
failed when converting the varchar value
'FlightDeck:161,CabinCrew:189' to data type int.
I just can't figure out why it keeps telling me this when I am not trying to convert a varchar to an int but rather ints to a single varchar.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[WorkDays] #requestedDate nchar(10)
AS
SET ANSI_WARNINGS OFF
DECLARE #date as nchar(10) = ''
DECLARE #returnVal as varchar(30) = ''
DECLARE #flightDeck as int = 0
DECLARE #cabinCrew as int = 0
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #date = #requestedDate
SELECT
#flightDeck = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Flight Deck' THEN 1 END),
#cabinCrew = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Cabin Crew' THEN 1 END)
FROM
dbo.CrewMember INNER JOIN
dbo.Crew_Category ON dbo.CrewMember.CrewCategorySeqNo = dbo.Crew_Category.CrewCategorySeqno
WHERE
(dbo.Crew_Category.Description = N'Flight Deck' OR
dbo.Crew_Category.Description = N'Cabin Crew') AND
(dbo.CrewMember.EmploymentEndDate > #date)
AND dbo.CrewMember.CrewSeqno NOT IN (
SELECT
CrewMember_1.CrewSeqno
FROM
dbo.CrewMember AS CrewMember_1 INNER JOIN
dbo.CrewReqAsg ON CrewMember_1.CrewSeqno = dbo.CrewReqAsg.crewSeqno INNER JOIN
dbo.activity ON dbo.CrewReqAsg.act_seqno = dbo.activity.act_seqno INNER JOIN
dbo.ActivityType ON dbo.activity.actType_seqno = dbo.ActivityType.actType_seqno INNER JOIN
dbo.ActivityCategory ON dbo.ActivityType.ActCat_seqno = dbo.ActivityCategory.actCat_seqno INNER JOIN
dbo.Crew_Category AS Crew_Category_1 ON CrewMember_1.CrewCategorySeqNo = Crew_Category_1.CrewCategorySeqno
WHERE (
dbo.ActivityCategory.Category = N'Ferie' OR
dbo.ActivityCategory.Category = N'Fridage' OR
dbo.ActivityCategory.Category = N'Sygdom') AND (Crew_Category_1.Description = N'Flight Deck' OR
Crew_Category_1.Description = N'Cabin Crew') AND (LEFT(dbo.activity.Start,10) LIKE #date));
SET #returnVal = 'FlightDeck:'+CAST(#flightDeck AS varchar);
SET #returnVal += ',CabinCrew:'+CAST(#cabinCrew AS varchar);
END
RETURN #returnVal
It's been a while since I've had to do this so perhaps I just forgot something fundamental. Please help me figure out why this happens? :)
Yes, you forgot something fundamental. To return data to the caller, use SELECT, not RETURN.
You need
SELECT #returnVal

Dyanamic SQL Query not working

I have a table called procedure look up which stores medical procedures
and have multiple company table for which i had to calculate the procedure fees so i had created a dynamic query for it
below is the query
declare #TableProviderName varchar(500)
,#SQLQuery1 nvarchar(max)
,#MaxRecordSize Int
,#Name varchar(250) = null
,#code varchar(50) = null
set #Name = 'sug'
set #TableProviderName = 'PRD_Tata_Details'
set #MaxRecordSize = 50
set #SQLQuery1 = '
;WITH CTE_Procedure AS
(
select top (#MaxRecordSize1)
GPL_ID_PK as ProcedureID
,GPL_ProcedureType as ProcedureType
,GPL_Code as ProcedureCode
,coalesce(Name,GPL_Name,null)as Procedurename
,GPL_CurrencyType_FK as CurrencyType
,ISNULL(GPL_Description,''NIL'') as ProcedureDescription
,ISNULL(GPL_PatientInstruction,''NIL'')as PatientInstructions
,GPL_ProcedureCategory_FK as ProcedureCategory
,GPL_CategorySpecialization_FK as ProcedureSpecialization
,coalesce(PatientPayable,GPL_ProcedureFee,0) as PatientPayable
,0 as InsurancePayable
,0 as InsuranceDiscount
,1 as ProcedureCount
,0 as IndBillingStatus
,Case
when GeneralProcedureID is not null then ''Insurance Supported''
else ''Insurance not Supported''
end as InsuranceStatus
,ROW_NUMBER( ) OVER ( ORDER BY GPL_Name ASC) as RowNumber
from
dbo.PRD_GeneralProcedure_Lookup
left join '
+ #TableProviderName +
'
on
GeneralProcedureID = GPL_ID_PK
where
GPL_ProcedureType = #ProcedureType1
and
(#Name1 is null or GPL_Name like %#Name1%)
and
(#code1 is null or GPL_Code like %#code1%)
)
Select
*
from
CTE_Procedure
'
Execute sp_executesql #SQLQuery1, N'#MaxRecordSize1 int, #ProcedureType1 tinyint,#Name1 varchar(250)
, #code varchar(50)' ,#MaxRecordSize1 = #MaxRecordSize, #ProcedureType1 = 1 , #Name1 = #Name, #code1 = #code
but when executing error occurs saying
"Incorrect syntax near '#Name1'"
can anyone help me with that where condition side issue
I think It may have something to do with your like statement and the way you pass the parameter.
Have a look at this question Parameters & Like statement.
#Name1 = "'%yourvalue%'"

SQL - Storing multiple records in a variable separated by commas

I have two variables, 1 varchar named cust_ref, and 1 int named associated_ids. What I'm trying to accomplish is the following:
You provide cust_ref with a value which will usually result in between 1+ rows being returned from the Customer table. I am concerned with gathering all customer_id records for that cust_ref and storing them in the associated_ids variable seperated by commars.
This is the SQL I have so far, and obviously is only loading one of the customer_id records into the variable. Based on this example I would like select #associated_ids to return the following 75458,77397,94955
declare #cust_ref varchar(20) = 'ABGR55532'
declare #associated_ids int
select distinct #associated_ids = customer_id
from dbo.Customer
where cust_ref = #cust_ref
select #associated_ids
select *
from dbo.Customer
where cust_ref = #cust_ref
Here is the results from the above, as you can see there are actually 3 associated_ids that I need stored in the variable in this example but my command is capturing the largest, I want all 3 seperated by commars.
declare #cust_ref varchar(20) = 'ABGR55532' --from your code
DECLARE #result varchar(100)
set #result =
(SELECT distinct (customer_id + ' ')
FROM dbo.Customer
where cust_ref = #cust_ref --from your code
ORDER BY (customer_id + ' ')
FOR XML PATH (''))
SELECT REPLACE(RTRIM(#result),' ',',')
You could try something like this ... obviously, some adjustment will be needed:
create table x (id varchar(50),num int)
insert into x (id,num) values ('75458','20')
insert into x (id,num) values ('77397','20')
insert into x (id,num) values ('94955','20')
and then,
create function GetList (#num as varchar(10))
returns varchar(100)
as
begin
declare #List varchar(100)
select #List = COALESCE(#List + ', ', '') + id
from x
where num = #num
return #List
end
Then, use something like this to get the values:
select distinct num,dbo.GetList(num) from x
Here you go
DECLARE #cust_ref varchar(20) = 'ABGR55532' --from your code
DECLARE #result varchar(100)
set #result =
(SELECT distinct (cast(customer_id as varchar) + ' ')
FROM dbo.Customer
where cust_ref = #cust_ref --from your code
ORDER BY (cast(customer_id as varchar) + ' ')
FOR XML PATH (''))
SELECT REPLACE(RTRIM(#result),' ',',')