I have a table in SQL Server 2005 as shown below
DECLARE #Promotions TABLE (PromoCode VARCHAR(10),
DeptID VARCHAR(10),
VerticalID VARCHAR(10),
BuildingID VARCHAR(10)
primary key (DeptID, VerticalID, BuildingID)
)
INSERT INTO #Promotions VALUES ('P1_20%','101','501','1001')
INSERT INTO #Promotions VALUES ('P2_10%','101','501','All')
INSERT INTO #Promotions VALUES ('P3_5%','101','All','All')
INSERT INTO #Promotions VALUES ('P1_50%','111','606','1002')
We need to find out promotion code value based on the matching values in DeptID, VerticalID and BuildingID columns. If there is not match with these 3 columns, look for a match with the input value for DeptID, VerticalID and a default value ("All") for BuildingID. Still if there is no result found, look for a match by considering input DeptID, default value for VerticalID ("All") and default value for BuildingID ("All").
The following query works fine – however it is using a 3 part approach. Can we achieve this in a single SQL query?
EXISTING CODE
DECLARE #PromoCode VARCHAR(10)
--Part 1
SELECT #PromoCode = PromoCode
FROM #Promotions
WHERE DeptID = #inputDeptID
AND VerticalID = #inputVerticalID
AND BuildingID = #inputBuildingID
--Part 2
IF #PromoCode IS NULL
BEGIN
SELECT #PromoCode = PromoCode
FROM #Promotions
WHERE DeptID = #inputDeptID
AND VerticalID = #inputVerticalID
AND BuildingID = 'All'
END
--Part 3
IF #PromoCode IS NULL
BEGIN
SELECT #PromoCode = PromoCode
FROM #Promotions
WHERE DeptID = #inputDeptID
AND VerticalID = 'All'
AND BuildingID = 'All'
END
--Result
SELECT #PromoCode
Testing
DECLARE #inputDeptID VARCHAR(10)
DECLARE #inputVerticalID VARCHAR(10)
DECLARE #inputBuildingID VARCHAR(10)
SET #inputDeptID = '101'
SET #inputVerticalID = '501'
SET #inputBuildingID = '3003'
Expected Result
P2_10%
You can approach this as a "prioritization". That is, look for all possible matches and then choose the one that is best, using order by and top:
set #PromoCode = (SELECT top 1 PromoCode
FROM #Promotions
WHERE DeptID = #inputDeptID AND
(VerticalID = #inputVerticalID OR VerticalId = 'ALL') AND
(BuildingID = #inputBuildingID OR BuildingID = 'ALL')
ORDER BY (case when Building = 'ALL' then 1 else 0 end),
(case when VerticalId = 'ALL' then 1 else 0 end)
);
There are other ways to express this. If performance is an issue and you have indexes on the columns, then a union all approach with order by may work better than ors in the where clause.
Related
I've put together the below example to explain what I'm trying to do.
I'm trying to within the query when #FeatureID is NULL then return all the records in the#Temp table for that ClientID. If a #FeatureID contains a 1 for example then returning the 1 record and the NULL record.
Where have I gone wrong in my where clause?
CREATE TABLE #Temp
(
ClientID int,
FeatureID int
)
Insert into #Temp
(
ClientID,
FeatureID
)
SELECT
1,
1
UNION
SELECT
1,
2
UNION
SELECT
1,
3
UNION
SELECT
1,
NULL
Declare #ClientID int = 1
Declare #FeatureID int = NULL
--should return all 4 records
select * from #Temp
where ClientID = 1 and
FeatureID = IIF(#FeatureID IS NULL, FeatureID, #FeatureID)
Set #ClientID = 1
Set #FeatureID = 1
--should return the 1,1 record and the 1,NULL record
select * from #Temp
where ClientID = 1 and
FeatureID = IIF(#FeatureID IS NULL, FeatureID, #FeatureID)
drop table #Temp
An alternative formulation that might be a little simpler:
select * from #Temp
where ClientID = 1 and
(ISNULL(#FeatureID, FeatureID) = FeatureID or FeatureID is NULL)
If #FeatureID is null, then FeatureID = FeatureID, which is always true. Otherwise, it will check for #FeatureID = FeatureID.
It will always return the rows where FeatureID is null.
You're effectively trying to compare NULL = NULL in your where clause which doesn't work. NULL does not compare equal to another NULL.
For your first query what you need to do is only compare the feature id column when #FeatureID is not null. This can be accomplished by testing the variable and using an OR condition.
--should return all 4 records
select * from #Temp
where ClientID = 1 and
(#FeatureID IS NULL OR FeatureID = #FeatureID)
In the second query you need to compare the feature ID column to both #FeatureID and NULL to get both rows.
--should return the 1,1 record and the 1,NULL record
select * from #Temp
where ClientID = 1 and
(#FeatureID IS NOT NULL AND (FeatureID IS NULL OR FeatureID=#FeatureID))
To handle both cases in a single query, use two conditions joined by OR that branched based on whether the variable is null or not.
select * from #Temp
where ClientID = 1 and
(
#FeatureID IS NULL
OR (#FeatureID IS NOT NULL AND (FeatureID IS NULL OR FeatureID=#FeatureID))
)
If you want to combine them then this should work:
select * from #Temp
where ClientID = 1 and
(#FeatureID is null
or
(#FeatureID is not null
and (FeatureID is null or FeatureID=#FeatureID)))
This will select all the records when #FeatureID = null and return 2 results {(1, null), (1, 1)} when #FeatureID = 1
I have a requirement to filter the table records based on passed input criteria to the SQL stored procedure. To make it simple, I'm elaborating the question using simple Employee table (I'm using MSSQL server for my project):
Table: Employee(Id, FirstName,LastName, Designation)
The stored procedure takes 3 input arguments (Id, LastName, Designation). This will be invoked with at-least one input parameter (other parameter will be set to empty string (in case of LastName, Designation) or 0 (In case of Id)).
Here is the stored procedure:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Create Table #employees (Id int, FirstName varchar(30),LastName varchar(30), Designation varchar(30);
IF(#id != 0)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Id = #id
END
IF(#lastName != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where LastName = #lastName
END
IF(designation != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Designation = #designation
END
-- Returning filtered record set to the application layer
Select Id,FirstName,LastName,Designation From #employees;
END
GO
I think there a lot of code repetition in the stored procedure. Is there an efficient way of solving this scenario ?
You may use a single insert query with a WHERE clause which covers all the logic:
BEGIN
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = #id AND #id <> 0) OR
(LastName = #lastName AND #lastName <> '') OR
(Designation = #designation AND #designation <> '');
You can use Case condition in Where clause, Find Below Query.
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = CASE WHEN #id = 0 THEN ID ELSE #ID END) OR
(LastName = CASE WHEN #lastName = '' THEN LastName ELSE #lastName END) OR
(Designation = CASE WHEN #designation = '' THEN Designation ELSe #designation END);
We can also Use AND Instead of OR in Where clause.
As #trincot said, there is no need for a temporary table. My suggestion is:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Select Id, FirstName,LastName,Designation From Employee
Where (#id>'' AND Id = #id)
OR (#lastName>'' AND LastName = #lastName)
OR (#designation>'' AND Designation = #designation);
END
GO
My id is not returning in the else if condition. It is showing that 'there is no row at position 0'.
USE [ctsdev]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter PROCEDURE [dbo].[usp_incen]
(
#ConsultantName varchar(50) ,
#ClientName varchar(50) ,
#StartDate varchar(50),
#PositionName varchar(20) ,
#Location varchar(20) ,
#Job_Status varchar (20),
#BenchMarketing varchar(1) ,
#Placement varchar(1),
#CompanyName varchar(20),
#Durations varchar(20),
#DurationofProject varchar(10),
#Last_Updated_Date nvarchar(50),
#Rec_Name varchar(50),
#id int output
)
AS
BEGIN
SET NOCOUNT ON
/* checking whether the row with same session name and some id and updated date with remaining every fields as NULL exists*/
if (SELECT COUNT(*) as cnt from tbl_Empincentivenew1 WHERE
id <> '' AND
Rec_Name = #Rec_Name and
Last_Updated_Date <> '' and
ConsultantName IS NULL and
ClientName IS NULL and
DurationofProject IS NULL and
Durations IS NULL and
StartDate IS NULL and
Location IS NULL and
BenchMarketing IS NULL and
Placement IS NULL and
CompanyName IS NULL)=0
BEGIN
/*if not then id field,recruitername and updated date is inserted*/
INSERT INTO [tbl_Empincentivenew1](ConsultantName,ClientName,PositionName,CompanyName,Location,DurationofProject,Durations,BenchMarketing,Placement,Job_Status,Last_Updated_Date,StartDate,Rec_Name)
OUTPUT INSERTED.id
values(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,#Last_Updated_Date,NULL,#Rec_Name)
SET #id=SCOPE_IDENTITY()
RETURN #id /*that id is returned to the front end*/
ENd
/*if the id with rec name,updated date with remaining all fiels null exist return that particular id to front end*/
ELSe if(SELECT COUNT(*) as cnt from tbl_Empincentivenew1 WHERE
id <> '' AND
Rec_Name = #Rec_Name and
Last_Updated_Date <> '' and
ConsultantName IS NULL and
ClientName IS NULL and
DurationofProject IS NULL and
Durations IS NULL and
StartDate IS NULL and
Location IS NULL and
BenchMarketing IS NULL and
Placement IS NULL and
CompanyName IS NULL)=1
BEGIN
SET #id=SCOPE_IDENTITY() /*return that existing id,instead for again inserting null values*/
RETURN #id
END
END
GO
This is my code for the first block the id value is getting inserted, second time whenever the same user logins he is not permitted to add new null records but the id for that null record is not returning.
SCOPE_IDENTITY() returns last generated ID. In your ELSE branch you are not creating any new records, so SCOPE_IDENTITY() remains NULL.
Your ELSE statement can look like this:
ELSE
SELECT #id = id
FROM tbl_Empincentivenew1
WHERE
Rec_Name = #Rec_Name and
Last_Updated_Date <> '' and
ConsultantName IS NULL and
ClientName IS NULL and
DurationofProject IS NULL and
Durations IS NULL and
StartDate IS NULL and
Location IS NULL and
BenchMarketing IS NULL and
Placement IS NULL and
CompanyName IS NULL
Although I would first run this SELECT and then if #id is still NULL - run the INSERT and return #id = SCOPE_IDENTITY().
I am still getting a weird error:
The select list for the INSERT statement contains more items than the insert list. The number of SELECT values must match the number of INSERT columns.
Code:
INSERT INTO #tab (Phone)
select t2.Phone
from
(
SELECT DISTINCT top 999 t3.Phone, MIN(t3.Ord)
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
) t2
The idea is to select all phone numbers from #tabTemp with their row order. Then I wanna distinct them and insert distincted numbers into table #tab. Top 999 is here only for order by purpose, because I use it into a function (UDF).
Structures are following:
declare #tabTemp TABLE
(
Phone1 varchar(128) NULL,
Phone2 varchar(128) NULL,
Ord int
);
declate #tab TABLE
(
Phone varchar(max) NULL
);
EDITED:
FULL CODE
CREATE FUNCTION dbo.myFnc(#PID int, #VID int, #JID int, #ColumnNo int)
RETURNS #tab TABLE
(
Phone varchar(max) NULL
)
AS
BEGIN
if #PID is null and #VID is null and #JID is null
return;
if #ColumnNo is null or (#ColumnNo<>2 and #ColumnNo<>3 and #ColumnNo<>6)
return;
declare #catH int;
set #catH = dbo.fncGetCategoryID('H','tt'); -- just returning int value
declare #kvalP int;
set #kvalP = dbo.fncGetCategoryID('P','te');
declare #kvalR int;
set #kvalR = dbo.fncGetCategoryID('R','te');
declare #tabTemp TABLE
(
Phone1 varchar(128) NULL,
Phone2 varchar(128) NULL,
Ord int
);
-- finding parent subject + current one
WITH subj AS(
SELECT *
FROM Subjekt
WHERE
(ID = #PID and #PID is not null)
or
(ID = #VID and #VID is not null)
or
(ID = #JID and #JID is not null)
UNION ALL
SELECT t.*
FROM Subjekt t
INNER JOIN subj r ON r.ID = t.ID
)
INSERT INTO #tabTemp (Phone1,Phone2)
(select
(case when o.TYP1=#catH then o.TEL1 else null end) Phone1
,(case when o.TYP2=#catH then o.TEL2 else null end) Phone2
,so.POR_C
from
subj s
,SubjektPerson so
,Persons o
,recSetup idS
,recSetup idSO
,recSetup idO
where 1=1
and idO.isValid=1
and idSO.isValid=1
and idS.isValid=1
and idSO.ID0=so.ID
and idS.ID0=s.ID
and idO.ID0=o.ID
and so.ID_PERSON=o.ID
and so.ID_SUBJECT=s.ID
and (o.TYP=#kvalP or o.TYP=#kvalR)
)
INSERT INTO #tab (Phone)
select t2.Phone
from
(
SELECT DISTINCT top 999 t3.Phone, MIN(t3.Ord)
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
) t2
RETURN
END
Not sure why you have distinct AND a group by on the same query. You could greatly simplify this.
INSERT INTO #tab (Phone)
SELECT top 999 t3.Phone
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
Now for the error message you were receiving, it doesn't seem like it came from this block of code because the syntax is fine and the number of columns matches correctly. I suspect the error is somewhere earlier in your code.
Also, you might want to consider using temp tables instead of table variables since it seems like you have a lot of rows in these tables.
You've focussed on the wrong insert. This is the one with the mismatch:
INSERT INTO #tabTemp (Phone1,Phone2)
(select
(case when o.TYP1=#catH then o.TEL1 else null end) Phone1
,(case when o.TYP2=#catH then o.TEL2 else null end) Phone2
,so.POR_C
from
...
Two columns in the insert list, 3 columns in the subselect. I can't tell just from the naming whether POR_C was meant to end up in the Ord column or not.
On the surface, it appears you are maybe triggering a query planner bug or something. There are a number of iffy things going on:
The union all of the same table to itself
Using both group by and distinct
I'm not sure what you mean by
Top 999 is here only for order by purpose, because I use it into a function (UDF).
Do you mean this whole query is executed within a UDF? If so, are there other queries that might be giving that error?
I have written a stored procedure in SQL Server 2000. I want a serial number for output table.
So when I run this stored proc I get this error:
An explicit value for the identity column in table
'#tmpSearchResults1' can only be specified when a column list is used
and IDENTITY_INSERT is ON.
I have tried with set IDENTITY_INSERT #tmpSearchResults1 on
Create Procedure dbo.usp_mobile_All_KeyWord(#searchkey varchar(30))
AS
CREATE TABLE #tmpSearchResults
(
property_id varchar(255),
property_number varchar(255),
auction_date_reason varchar(255)
)
INSERT INTO #tmpSearchResults
SELECT
p.property_id, p.property_number, p.auction_date_reason
FROM
Pr p
INNER JOIN
Au a ON p.auction_id = a.auction_id
INNER JOIN
PrAdd pa ON p.property_id = pa.property_id
INNER JOIN state AS s ON s.state_id=pa.state
where
(
(p.archive = 'N'
AND
a.show_on_site = 'Y'
AND
(
(
((p.auction_date >= CONVERT(datetime, CONVERT(varchar, GETDATE(), 103), 103) and (p.auction_date_reason is null or p.auction_date_reason = ''))
or
(p.auction_date <= CONVERT(datetime, CONVERT(varchar, GETDATE(), 103), 103) and ( p.auction_date_reason = 'Accepting Offers' )))
and
pa.property_address_type_id = 1 )) )
and
(state_abbreviation=#searchkey or s.state_name like '%'+''+ #searchkey +''+'%' or city like '%'+''+ #searchkey +''+'%' or pa.address1 like '%'+''+ #searchkey +''+'%'
or pa.address2 like '%'+''+ #searchkey +''+'%')
)
)
CREATE TABLE #tmpSearchResults1
(
i1 int identity,
property_id varchar(255),
property_number varchar(255),
auction_date_reason varchar(255)
)
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
order by
case when charindex(#searchkey,state) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,statename) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,city) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,address2) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,address1) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,short_description) >0 then 1000 else 0 end desc
select * from #tmpSearchResults1
Plz do help me
The error code is very very very clear.
The relevant portion is ...when a column list is used....
You need to specify your column list in the INSERT statement.
INSERT INTO #tmpSearchResults
(i1,
property_id,
property_number,
auction_date_reason)
SELECT
p.property_id, p.property_number, p.auction_date_reason
FROM...
First, there is a comma too much in the SELECT part of your second statement:
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason , <-- THIS ONE!!
from #tmpSearchResults
The last column of a SELECT statement must be without a comma.
So this would be correct:
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
Second, did you read this part of the error message?
An explicit value [...] can only be specified when a column list is used
The "column list" part means that you have to specify the columns in the INSERT part:
insert into #tmpSearchResults1
(property_id, property_number, auction_date_reason)
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
You can get away with not specifying the columns when the number of columns in the SELECT statement is the same as in the table in which they should be inserted (and if the data types match).
If one of these conditions is not met, you need to specify the columns because otherwise SQL Server doesn't know which value to insert into which column.