SQL Server: Update, set a value that is within another select - sql

I have the following query:
update UpdCmmPartnerArticleGroup
set IsActive = 1,
Name = a.GroupName
from
(
select t.Name as GroupName
from #ArticleGroupsTable t
left join UpdCmmPartnerArticleGroup s on s.PartnerID=t.PartnerID and s.Name=t.Name
where s.PartnerID is null
) a
where Name = '' and IsActive = 0
It's purpose is to take a list of GroupNames and assign them to the UpdCmmPartnerArticleGroup.Name field. The list has 8 values and it uses only the first and the last to update.
What am I doing wrong, it looks pretty straight forward, but it's behavior is strange.

You can use a little bit simplified version.
UPDATE u
SET IsActive = 1, Name = t.Name
FROM #ArticleGroupsTable t
LEFT JOIN UpdCmmPartnerArticleGroup u
ON u.PartnerID=t.PartnerID AND u.Name=t.Name
WHERE u.Name = '' and u.IsActive = 0 and u.PartnerID IS NULL

Why not use subquery
update s
set IsActive = 1,
Name = (select t.Name from #ArticleGroupsTable t WHERE s.PartnerID=t.PartnerID and s.Name=t.Name AND s.PartnerID is null)
from UpdCmmPartnerArticleGroup s
where Name = '' and IsActive = 0

Try this,
UPDATE s
SET s.IsActive = 1
,s.NAME = t.NAME
FROM #ArticleGroupsTable t
LEFT JOIN UpdCmmPartnerArticleGroup s ON s.PartnerID = t.PartnerID
AND s.NAME = t.NAME
WHERE s.PartnerID IS NULL
AND s.NAME = ''
AND s.IsActive = 0

Try this:
IF OBJECT_ID('tempdb..#UpdCmmPartnerArticleGroup') IS NOT NULL
DROP TABLE #UpdCmmPartnerArticleGroup;
IF OBJECT_ID('tempdb..#ArticleGroupsTable') IS NOT NULL
DROP TABLE #ArticleGroupsTable;
CREATE TABLE #UpdCmmPartnerArticleGroup
(
PartnerId INT IDENTITY(1, 1)
,Name VARCHAR(50)
,IsActive BIT
);
INSERT INTO [#UpdCmmPartnerArticleGroup]
( [Name], [IsActive] )
VALUES ( 'Test1' -- Name - varchar(50)
, 1 -- IsActive - bit
),
( 'Test2' -- Name - varchar(50)
, 0 -- IsActive - bit
),
( '' -- Name - varchar(50)
, 0 -- IsActive - bit
);
CREATE TABLE #ArticleGroupsTable
(
PartnerId INT
,Name VARCHAR(50)
);
INSERT INTO [#ArticleGroupsTable]
( [PartnerId], [Name] )
VALUES ( 1, 'Test1' ),
( 2, 'Test2' ),
( 4, 'Test4' );
SELECT *
FROM [#UpdCmmPartnerArticleGroup];
SELECT *
FROM [#ArticleGroupsTable];
UPDATE [#UpdCmmPartnerArticleGroup]
SET [IsActive] = 1
, [Name] = (
SELECT t.[Name] AS GroupName
FROM [#ArticleGroupsTable] t
LEFT JOIN [#UpdCmmPartnerArticleGroup] s
ON [s].[PartnerId] = [t].[PartnerId]
AND [s].[Name] = [t].[Name]
WHERE [s].[PartnerId] IS NULL
)
WHERE [#UpdCmmPartnerArticleGroup].[Name] = ''
AND [IsActive] = 0;
SELECT *
FROM [#UpdCmmPartnerArticleGroup];

Related

CASE WHEN to assign a result set to a query

I've been trying to assign different resultsets to a query depending on an input
Depending on the company selected a set of filtered results should be displayed, here's my code.
DECLARE #COMPANY VARCHAR(10) = 'Company_name1'
SELECT
(CASE
WHEN #COMPANY = 'Company_name1'
THEN (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (INV.DATAAREAID = #COMPANY)
AND (((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
AND (INV.INVENTLOCATIONID = 'x5'))
WHEN #COMPANY = 'Company_name2'
THEN (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (INV.DATAAREAID = #COMPANY)
AND (((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
AND (INVENTLOCATIONID IN ('X0', 'X1', 'X2', 'X3', 'X5', 'X6', 'X8', 'P6', 'P8')))
ELSE (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (DATAAREAID = #COMPANY)
AND ((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
END) AS WHAREWOUSE
FROM
INVENTDIM INV
ORDER BY
INV.INVENTLOCATIONID
Why am I not allowed to do this?
Here's one way you could do this using a CASE expression. Use a constant to reject entries where the company name matches the specific cases and the location is not in your list. If the company isn't one of the special cases, then accept it.
DECLARE #COMPANY VARCHAR(10) = 'Company_name1';
SELECT WHAREHOUSE = INVENTLOCATIONID
FROM dbo.INVENTDIM
WHERE DATAAREAID = #COMPANY
AND INVENTLOCATIONID IS NOT NULL
AND WMSLOCATIONID IS NOT NULL
AND WMSLOCATIONID <> ''
AND 1 =
(
CASE
WHEN DATAAREAID = 'Company_name1'
AND INVENTLOCATIONID <> 'x5'
THEN 0
WHEN DATAAREAID = 'Company_name2'
AND INVENTLOCATIONID NOT IN ('X0','X1','X2','X3','X5','X6','X8','P6','P8')
THEN 0
ELSE 1
END
)
ORDER BY INVENTLOCATIONID;
Potentially a disaster for query plan reuse, though, unless DATAAREAID is unique.
This is definitely a better approach:
Can you create a new cross reference table to allow the database to map company to the inventlocationid? – Zynon Putney II
So say you had a mapping table like:
CREATE TABLE dbo.LocationMap
(
DATAAREAID varchar(10),
INVENTLOCATIONID char(2),
PRIMARY KEY(DATAAREAID, INVENTLOCATIONID
);
INSERT dbo.LocationMap(DATAAREAID,INVENTLOCATIONID)
VALUES
('Company_Name1', 'x5'),
('Company_Name2', 'X0'), ('Company_Name2', 'X1') -- , ...
Then your query becomes:
DECLARE #COMPANY VARCHAR(10) = 'Company_name1';
SELECT WHAREHOUSE = INV.INVENTLOCATIONID
FROM dbo.INVENTDIM AS INV
LEFT OUTER JOIN dbo.LocationMap AS lm
ON INV.DATAAREAID = lm.DATAAREAID
WHERE INV.DATAAREAID = #COMPANY
AND INV.INVENTLOCATIONID IS NOT NULL
AND INV.WMSLOCATIONID IS NOT NULL
AND INV.WMSLOCATIONID <> ''
AND (lm.INVENTLOCATIONID = INV.INVENTLOCATIONID
OR lm.DATAAREAID IS NULL)
ORDER BY INV.INVENTLOCATIONID;
Written as a single query this is equivalent logic. Depending on the size of your data it's possible that the if..else branching would work better.
SELECT INVENTLOCATINID
FROM INVENTDIM INV
WHERE
INV.DATAAREAID = #COMPANY AND INVENTLOCATIONID IS NOT NULL WMSLOCATIONID <> '' AND
(
#COMPANY = 'Company name1' AND INV.INVENTLOCATIONID = 'x5' OR
#COMPANY = 'Company_name2' AND INVENTLOCATIONID IN
('X0', 'X1','X2','X3','X5','X6','X8','P6','P8') OR
#COMPANY NOT IN ('Company name1', 'Company name2')
);
Try this below query to get desired output
DECLARE #COMPANY VARCHAR(10) = 'Company_name1'
declare #tblIL as table
(
INVENTLOCATIONID int
)
IF(#COMPANY = 'Company_name1')
BEGIN
insert into #tblIL(INVENTLOCATIONID) values ('x5')
END
ELSE IF(#COMPANY = 'Company_name2')
BEGIN
insert into #tblIL(INVENTLOCATIONID) values ('X0', 'X1','X2','X3','X5','X6','X8','P6','P8')
END
ELSE
BEGIN
insert into #tblIL(INVENTLOCATIONID) values SELECT DISTINCT INVENTLOCATIONID FROM INVENTDIM
END
SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE INV.DATAAREAID = #COMPANY AND INVENTLOCATIONID IS NOT NULL AND WMSLOCATIONID IS NOT NULL AND WMSLOCATIONID <> ''
AND INV.INVENTLOCATIONID in (select INVENTLOCATIONID from #tblIL)

What is an elegant way to use a column of sql output for the tag of xml output within mssql?

I have a stored procedure that returns xml.
Sample call is
get_organization_user_export_data_permission_procedure
#organizationId = '64483342-af49-4a04-b25d-ce7346cb5375'
, #userId = '5324B48E-B560-4345-B290-12CC72D722FA'
Sample output is
<root>
<all>false</all>
<analytics>false</analytics>
<none>true</none>
</root>
Which is correct.
The base SQL is
select lower([type]) name,
value permission,
count(permissionType) found
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = '64483342-af49-4a04-b25d-ce7346cb5375'
and userId = '5324B48E-B560-4345-B290-12CC72D722FA' ) permission
on export.value = permission.permissionType
group by [type], value
which returns
name permission found
none 0 1
all 1 0
analytics 2 0
The problem is I've hardcoded the element tag names within the stored procedure.
This is the stored procedure which shows I've got a separate subquery for each of the possible element tag names.
CREATE PROCEDURE [dbo].[get_organization_user_export_data_permission_procedure]
(
#organizationId uniqueidentifier
, #userId uniqueidentifier
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
if (
select count(*) from [OrganizationUserDataPermission]
where organizationId = #organizationId
and userId = #userId
) = 0 begin
insert into [OrganizationUserDataPermission] ( id, organizationId, userId, permissionType, createDateTime )
select newId(),#organizationId organizationId, #userId userId, 0 permissionType, getDate()
end
declare #result XML
set #result = (
select '',
(
select case true when 0 then 'false' else 'true' end 'all' from (
select lower([type]) name,
value permission,
count(permissionType) true
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = #organizationId
and userId = #userId ) permission
on export.value = permission.permissionType
where value = 1
group by [type], value
) [all]
) 'all'
, (
select case true when 0 then 'false' else 'true' end 'analytics' from (
select lower([type]) name,
value permission,
count(permissionType) true
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = #organizationId
and userId = #userId ) permission
on export.value = permission.permissionType
where value = 2
group by [type], value
) [analytics]
) 'analytics'
, (
select case true when 0 then 'false' else 'true' end 'none' from (
select lower([type]) name,
value permission,
count(permissionType) true
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = #organizationId
and userId = #userId ) permission
on export.value = permission.permissionType
where value = 0
group by [type], value
) [none]
) 'none'
for xml path(''), root('root')
)
select #result result
END
GO
At the moment if another export permission where introduced I'd have to alter the stored procedure (and the view). Is there an elegant way ... by elegant I mean without using dynamic sql ... to produce the same xml results without knowing beforehand what the permissions might be?
Supporting schema.
The view ... named so that it marries to the c# code it relates to.
CREATE VIEW [dbo].[GroupExportRestrictionTypeConstants_RestrictionType_view] AS
select 'All' [type], 1 [value]
union
select 'Analytics' [type], 2 [value]
union
select 'None' [type], 0 [value]
The table without foreign keys.
CREATE TABLE [dbo].[OrganizationUserDataPermission](
[Id] [uniqueidentifier] NOT NULL,
[OrganizationId] [uniqueidentifier] NOT NULL,
[UserId] [uniqueidentifier] NOT NULL,
[PermissionType] [int] NOT NULL,
[CreateDateTime] [datetime] NOT NULL,
CONSTRAINT [PK_OrganizationUserDataPermission] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
-- some sample data
insert into [OrganizationUserDataPermission] ( id, organizationId, userId, permissionType, createDateTime )
select newId(),'64483342-af49-4a04-b25d-ce7346cb5375' organizationId, '5324B48E-B560-4345-B290-12CC72D722FA' userId, 1 permissionType, getDate()
I place this as another answer, as it follows a completely different approach:
In general it is impossible, to define a column alias dynamically. The element names in an XML are taken from the column names, so this applies here too.
But
An XML is technically seen a string. Normally I would not advise to build XML on string base, but - as long as you are sure, that your element's names will never contain any forbidden characters, this is a solution, which is truly generically taken from the data given:
DECLARE #tbl TABLE(name VARCHAR(100),permission INT,found BIT);
INSERT INTO #tbl VALUES
('none',0,1)
,('all',1,0)
,('analytics',2,0);
SELECT CAST('<root>'
+ (
SELECT '<' + name + '>' + CASE WHEN found=1 THEN 'true' ELSE 'false' END + '</' + name + '>'
FROM #tbl
FOR XML PATH(''),TYPE
).value('.','varchar(max)')
+'</root>' AS XML)
One thing I can tell you for sure: This is much to complicated!
One thing you could change easily, was to transfer the identical sub-select into one CTE.
But - if I understand this correctly - this can be done much easier:
You tell me
The base SQL is
select lower([type]) name,
value permission,
count(permissionType) found
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = '64483342-af49-4a04-b25d-ce7346cb5375'
and userId = '5324B48E-B560-4345-B290-12CC72D722FA' ) permission
on export.value = permission.permissionType
group by [type], value
which returns
name permission found
none 0 1
all 1 0
analytics 2 0
Assuming, the returned values are OK, you can go with conditional aggregation:
A mock-up table to simulate your final result from above
DECLARE #tbl TABLE(name VARCHAR(100),permission INT,found BIT);
INSERT INTO #tbl VALUES
('none',0,1)
,('all',1,0)
,('analytics',2,0);
--The query
SELECT MAX(CASE WHEN(name='all') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [all]
,MAX(CASE WHEN(name='analytics') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [analytics]
,MAX(CASE WHEN(name='none') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [none]
FROM #tbl
GROUP BY name
FOR XML PATH(''),ROOT('root');
The result
<root>
<all>false</all>
<analytics>false</analytics>
<none>true</none>
</root>
For your case this migth be this
WITH MyQuery aS
(
select lower([type]) name,
value permission,
count(permissionType) found
from [GroupExportRestrictionTypeConstants_RestrictionType_view] export
left join (
select permissionType from [OrganizationUserDataPermission]
where organizationId = '64483342-af49-4a04-b25d-ce7346cb5375'
and userId = '5324B48E-B560-4345-B290-12CC72D722FA' ) permission
on export.value = permission.permissionType
group by [type], value
)
SELECT MAX(CASE WHEN(name='all') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [all]
,MAX(CASE WHEN(name='analytics') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [analytics]
,MAX(CASE WHEN(name='none') THEN CASE WHEN found=1 THEN 'true' ELSE 'false' END END) AS [none]
FROM MyQuery
GROUP BY name
FOR XML PATH(''),ROOT('root');

How to traverse a path in a table with id & parentId?

Suppose I have a table like:
id | parentId | name
1 NULL A
2 1 B
3 2 C
4 1 E
5 3 E
I am trying to write a scalar function I can call as:
SELECT dbo.GetId('A/B/C/E') which would produce "5" if we use the above reference table. The function would do the following steps:
Find the ID of 'A' which is 1
Find the ID of 'B' whose parent is 'A' (id:1) which would be id:2
Find the ID of 'C' whose parent is 'B' (id:2) which would be id:3
Find the ID of 'E' whose parent is 'C' (id:3) which would be id:5
I was trying to do it with a WHILE loop but it was getting very complicated very fast... Just thinking there must be a simple way to do this.
CTE version is not optimized way to get the hierarchical data. (Refer MSDN Blog)
You should do something like as mentioned below. It's tested for 10 millions of records and is 300 times faster than CTE version :)
Declare #table table(Id int, ParentId int, Name varchar(10))
insert into #table values(1,NULL,'A')
insert into #table values(2,1,'B')
insert into #table values(3,2,'C')
insert into #table values(4,1,'E')
insert into #table values(5,3,'E')
DECLARE #Counter tinyint = 0;
IF OBJECT_ID('TEMPDB..#ITEM') IS NOT NULL
DROP TABLE #ITEM
CREATE TABLE #ITEM
(
ID int not null
,ParentID int
,Name VARCHAR(MAX)
,lvl int not null
,RootID int not null
)
INSERT INTO #ITEM
(ID,lvl,ParentID,Name,RootID)
SELECT Id
,0 AS LVL
,ParentId
,Name
,Id AS RootID
FROM
#table
WHERE
ISNULL(ParentId,-1) = -1
WHILE ##ROWCOUNT > 0
BEGIN
SET #Counter += 1
insert into #ITEM(ID,ParentId,Name,lvl,RootID)
SELECT ci.ID
,ci.ParentId
,ci.Name
,#Counter as cntr
,ch.RootID
FROM
#table AS ci
INNER JOIN
#ITEM AS pr
ON
CI.ParentId=PR.ID
LEFT OUTER JOIN
#ITEM AS ch
ON ch.ID=pr.ID
WHERE
ISNULL(ci.ParentId, -1) > 0
AND PR.lvl = #Counter - 1
END
select * from #ITEM
Here is an example of functional rcte based on your sample data and requirements as I understand them.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
id int
, parentId int
, name char(1)
)
insert #Something
select 1, NULL, 'A' union all
select 2, 1, 'B' union all
select 3, 2, 'C' union all
select 4, 1, 'E' union all
select 5, 3, 'E'
declare #Root char(1) = 'A';
with MyData as
(
select *
from #Something
where name = #Root
union all
select s.*
from #Something s
join MyData d on d.id = s.parentId
)
select *
from MyData
Note that if you change the value of your variable the output will adjust. I would make this an inline table valued function.
I think I have it based on #SeanLange's recommendation to use a recursive CTE (above in the comments):
CREATE FUNCTION GetID
(
#path VARCHAR(MAX)
)
/* TEST:
SELECT dbo.GetID('A/B/C/E')
*/
RETURNS INT
AS
BEGIN
DECLARE #ID INT;
WITH cte AS (
SELECT p.id ,
p.parentId ,
CAST(p.name AS VARCHAR(MAX)) AS name
FROM tblT p
WHERE parentId IS NULL
UNION ALL
SELECT p.id ,
p.parentId ,
CAST(pcte.name + '/' + p.name AS VARCHAR(MAX)) AS name
FROM dbo.tblT p
INNER JOIN cte pcte ON
pcte.id = p.parentId
)
SELECT #ID = id
FROM cte
WHERE name = #path
RETURN #ID
END

How to correctly update parent table based on multiple child table records when not using loops

How to correctly update parent table based on multiple child table records when not using loops?
The solution below uses two temp tables and uses loops to give back correct balance.
#customer-balance is the master table while #CUSTOMER_DEBIT_ENTRIES is the table with more than one entry.
Is there some other way?
IF OBJECT_ID ('tempdb.dbo.#CUSTOMER_BALANCE') IS NOT NULL
DROP TABLE #CUSTOMER_BALANCE
IF OBJECT_ID ('tempdb.dbo.#CUSTOMER_DEBIT_ENTRIES') IS NOT NULL
DROP TABLE #CUSTOMER_DEBIT_ENTRIES
create table #CUSTOMER_BALANCE
([RECNUM] decimal(8,0) IDENTITY(1,1) NOT NULL,
[CUSTOMER_ID][nchar](8) NOT NULL,
[CUST_BALANCE] [decimal] (14,4) DEFAULT((0))
PRIMARY KEY CLUSTERED(RECNUM)
);
INSERT INTO #CUSTOMER_BALANCE
SELECT 'NGR',1500
UNION
SELECT 'ZGR',100
UNION
SELECT 'MKR',1000
UNION
SELECT 'DKR',1500
GO
SELECT * FROM #CUSTOMER_BALANCE
;create table #CUSTOMER_DEBIT_ENTRIES
([RECNUM] decimal(8,0) IDENTITY(1,1) NOT NULL,
[CUSTOMER_ID][nchar](8) NOT NULL,
[DEBIT_ENTRY] [decimal] (14,4) DEFAULT((0)),
[PROCESS_FLG] bit default 0,
PRIMARY KEY CLUSTERED(RECNUM)
);
INSERT INTO #CUSTOMER_DEBIT_ENTRIES
SELECT 'NGR',500,0
UNION
SELECT 'ZGR',10,0
UNION
SELECT 'MKR',100,0
UNION
SELECT 'DKR',500,0
UNION
SELECT 'NGR',200,0
UNION
SELECT 'ZGR',20,0
Go
SELECT RECNUM,'#CUSTOMER_BALANCE' AS TABLE_NAME,CUSTOMER_ID,CUST_BALANCE FROM #CUSTOMER_BALANCE
SELECT RECNUM,'#CUSTOMER_DEBIT_ENTRIES' AS TABLE_NAME,CUSTOMER_ID,DEBIT_ENTRY,PROCESS_FLG FROM #CUSTOMER_DEBIT_ENTRIES
-- WRONG RESULT BELOW
Update #CUSTOMER_BALANCE
SET CUST_BALANCE = c.CUST_BALANCE - d.DEBIT_ENTRY
FROM #CUSTOMER_BALANCE c inner join #CUSTOMER_DEBIT_ENTRIES d
on c.CUSTOMER_ID = d.CUSTOMER_ID
--CORRECT RESULTS BELOW USING WHILE LOOPS
DECLARE #counter INT, #counter1 INT, #RECNUM INT
SET #counter = 0
SET #RECNUM = 0
SET #counter1 = (SELECT COUNT(*) FROM #CUSTOMER_DEBIT_ENTRIES WHERE PROCESS_FLG = 0)
WHILE #counter < #counter1
BEGIN
SET #RECNUM = (Select Top 1 RECNUM FROM #CUSTOMER_DEBIT_ENTRIES where PROCESS_FLG = 0)
UPDATE #CUSTOMER_BALANCE
SET CUST_BALANCE = c.CUST_BALANCE - d.DEBIT_ENTRY
FROM #CUSTOMER_BALANCE c inner join (SELECT TOP 1 RECNUM, CUSTOMER_ID,DEBIT_ENTRY,PROCESS_FLG FROM #CUSTOMER_DEBIT_ENTRIES
WHERE RECNUM = #RECNUM AND PROCESS_FLG = 0) d on c.CUSTOMER_ID = d.CUSTOMER_ID
UPDATE #CUSTOMER_DEBIT_ENTRIES
SET PROCESS_FLG = 1 WHERE RECNUM = #RECNUM
set #counter = #counter + 1
END
You need to reduce the number of rows per PK-value to one, in your case using aggregation:
Update #CUSTOMER_BALANCE
SET CUST_BALANCE = c.CUST_BALANCE - d.DEBIT_ENTRY
FROM #CUSTOMER_BALANCE c
inner join
( select CUSTOMER_ID, sum(DEBIT_ENTRY) as DEBIT_ENTRY
from #CUSTOMER_DEBIT_ENTRIES
GROUP BY CUSTOMER_ID
) as d
on c.CUSTOMER_ID = d.CUSTOMER_ID
This can be achieved using CTE:
;WITH CTE AS
( SELECT customer_id, SUM(debit_entry) AS debit_entry
FROM #CUSTOMER_DEBIT_ENTRIES
GROUP BY CUSTOMER_ID
)
UPDATE #CUSTOMER_BALANCE
SET CUST_BALANCE = CB.CUST_BALANCE - cte.DEBIT_ENTRY
FROM CTE INNER JOIN #CUSTOMER_BALANCE CB
ON CB.CUSTOMER_ID = CTE.CUSTOMER_ID

SQL Switch/Case in 'where' clause

I tried searching around, but I couldn't find anything that would help me out.
I'm trying to do this in SQL:
declare #locationType varchar(50);
declare #locationID int;
SELECT column1, column2
FROM viewWhatever
WHERE
CASE #locationType
WHEN 'location' THEN account_location = #locationID
WHEN 'area' THEN xxx_location_area = #locationID
WHEN 'division' THEN xxx_location_division = #locationID
I know that I shouldn't have to put '= #locationID' at the end of each one, but I can't get the syntax even close to being correct. SQL keeps complaining about my '=' on the first WHEN line...
How can I do this?
declare #locationType varchar(50);
declare #locationID int;
SELECT column1, column2
FROM viewWhatever
WHERE
#locationID =
CASE #locationType
WHEN 'location' THEN account_location
WHEN 'area' THEN xxx_location_area
WHEN 'division' THEN xxx_location_division
END
without a case statement...
SELECT column1, column2
FROM viewWhatever
WHERE
(#locationType = 'location' AND account_location = #locationID)
OR
(#locationType = 'area' AND xxx_location_area = #locationID)
OR
(#locationType = 'division' AND xxx_location_division = #locationID)
Here you go.
SELECT
column1,
column2
FROM
viewWhatever
WHERE
CASE
WHEN #locationType = 'location' AND account_location = #locationID THEN 1
WHEN #locationType = 'area' AND xxx_location_area = #locationID THEN 1
WHEN #locationType = 'division' AND xxx_location_division = #locationID THEN 1
ELSE 0
END = 1
I'd say this is an indicator of a flawed table structure. Perhaps the different location types should be separated in different tables, enabling you to do much richer querying and also avoid having superfluous columns around.
If you're unable to change the structure, something like the below might work:
SELECT
*
FROM
Test
WHERE
Account_Location = (
CASE LocationType
WHEN 'location' THEN #locationID
ELSE Account_Location
END
)
AND
Account_Location_Area = (
CASE LocationType
WHEN 'area' THEN #locationID
ELSE Account_Location_Area
END
)
And so forth... We can't change the structure of the query on the fly, but we can override it by making the predicates equal themselves out.
EDIT: The above suggestions are of course much better, just ignore mine.
The problem with this is that when the SQL engine goes to evaluate the expression, it checks the FROM portion to pull the proper tables, and then the WHERE portion to provide some base criteria, so it cannot properly evaluate a dynamic condition on which column to check against.
You can use a WHERE clause when you're checking the WHERE criteria in the predicate, such as
WHERE account_location = CASE #locationType
WHEN 'business' THEN 45
WHEN 'area' THEN 52
END
so in your particular case, you're going to need put the query into a stored procedure or create three separate queries.
OR operator can be alternative of case when in where condition
ALTER PROCEDURE [dbo].[RPT_340bClinicDrugInventorySummary]
-- Add the parameters for the stored procedure here
#ClinicId BIGINT = 0,
#selecttype int,
#selectedValue varchar (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
drugstock_drugname.n_cur_bal,drugname.cdrugname,clinic.cclinicname
FROM drugstock_drugname
INNER JOIN drugname ON drugstock_drugname.drugnameid_FK = drugname.drugnameid_PK
INNER JOIN drugstock_drugndc ON drugname.drugnameid_PK = drugstock_drugndc.drugnameid_FK
INNER JOIN drugndc ON drugstock_drugndc.drugndcid_FK = drugndc.drugid_PK
LEFT JOIN clinic ON drugstock_drugname.clinicid_FK = clinic.clinicid_PK
WHERE (#ClinicId = 0 AND 1 = 1)
OR (#ClinicId != 0 AND drugstock_drugname.clinicid_FK = #ClinicId)
-- Alternative Case When You can use OR
AND ((#selecttype = 1 AND 1 = 1)
OR (#selecttype = 2 AND drugname.drugnameid_PK = #selectedValue)
OR (#selecttype = 3 AND drugndc.drugid_PK = #selectedValue)
OR (#selecttype = 4 AND drugname.cdrugclass = 'C2')
OR (#selecttype = 5 AND LEFT(drugname.cdrugclass, 1) = 'C'))
ORDER BY clinic.cclinicname, drugname.cdrugname
END
Please try this query.
Answer To above post:
select #msgID, account_id
from viewMailAccountsHeirachy
where
CASE #smartLocationType
WHEN 'store' THEN account_location
WHEN 'area' THEN xxx_location_area
WHEN 'division' THEN xxx_location_division
WHEN 'company' THEN xxx_location_company
END = #smartLocation
Try this:
WHERE (
#smartLocationType IS NULL
OR account_location = (
CASE
WHEN #smartLocationType IS NOT NULL
THEN #smartLocationType
ELSE account_location
END
)
)
CREATE PROCEDURE [dbo].[Temp_Proc_Select_City]
#StateId INT
AS
BEGIN
SELECT * FROM tbl_City
WHERE
#StateID = CASE WHEN ISNULL(#StateId,0) = 0 THEN 0 ELSE StateId END ORDER BY CityName
END
Try this query, it's very easy and useful: Its ready to execute!
USE tempdb
GO
IF NOT OBJECT_ID('Tempdb..Contacts') IS NULL
DROP TABLE Contacts
CREATE TABLE Contacts(ID INT, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO Contacts (ID, FirstName, LastName)
SELECT 1, 'Omid', 'Karami'
UNION ALL
SELECT 2, 'Alen', 'Fars'
UNION ALL
SELECT 3, 'Sharon', 'b'
UNION ALL
SELECT 4, 'Poja', 'Kar'
UNION ALL
SELECT 5, 'Ryan', 'Lasr'
GO
DECLARE #FirstName VARCHAR(100)
SET #FirstName = 'Omid'
DECLARE #LastName VARCHAR(100)
SET #LastName = ''
SELECT FirstName, LastName
FROM Contacts
WHERE
FirstName = CASE
WHEN LEN(#FirstName) > 0 THEN #FirstName
ELSE FirstName
END
AND
LastName = CASE
WHEN LEN(#LastName) > 0 THEN #LastName
ELSE LastName
END
GO
In general you can manage case of different where conditions in this way
SELECT *
FROM viewWhatever
WHERE 1=(CASE <case column or variable>
WHEN '<value1>' THEN IIF(<where condition 1>,1,0)
WHEN '<value2>' THEN IIF(<where condition 2>,1,0)
ELSE IIF(<else condition>,1,0)
END)
Case Statement in SQL Server Example
Syntax
CASE [ expression ]
WHEN condition_1 THEN result_1
WHEN condition_2 THEN result_2
...
WHEN condition_n THEN result_n
ELSE result
END
Example
SELECT contact_id,
CASE website_id
WHEN 1 THEN 'TechOnTheNet.com'
WHEN 2 THEN 'CheckYourMath.com'
ELSE 'BigActivities.com'
END
FROM contacts;
OR
SELECT contact_id,
CASE
WHEN website_id = 1 THEN 'TechOnTheNet.com'
WHEN website_id = 2 THEN 'CheckYourMath.com'
ELSE 'BigActivities.com'
END
FROM contacts;
This worked for me.
CREATE TABLE PER_CAL ( CAL_YEAR INT, CAL_PER INT )
INSERT INTO PER_CAL( CAL_YEAR, CAL_PER ) VALUES ( 20,1 ), ( 20,2 ), ( 20,3 ), ( 20,4 ), ( 20,5 ), ( 20,6 ), ( 20,7 ), ( 20,8 ), ( 20,9 ), ( 20,10 ), ( 20,11 ), ( 20,12 ),
( 99,1 ), ( 99,2 ), ( 99,3 ), ( 99,4 ), ( 99,5 ), ( 99,6 ), ( 99,7 ), ( 99,8 ), ( 99,9 ), ( 99,10 ), ( 99,11 ), ( 99,12 )
The 4 digit century is determined by the rule, if the year is 50 or more, the century is 1900, otherwise 2000.
Given two 6 digit periods that mark the start and end period, like a quarter, return the rows that fall in that range.
-- 1st quarter of 2020
SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 202001 AND 202003
-- 4th quarter of 1999
SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 199910 AND 199912
Try this query. Its very easy to understand:
CREATE TABLE PersonsDetail(FirstName nvarchar(20), LastName nvarchar(20), GenderID int);
GO
INSERT INTO PersonsDetail VALUES(N'Gourav', N'Bhatia', 2),
(N'Ramesh', N'Kumar', 1),
(N'Ram', N'Lal', 2),
(N'Sunil', N'Kumar', 3),
(N'Sunny', N'Sehgal', 1),
(N'Malkeet', N'Shaoul', 3),
(N'Jassy', N'Sohal', 2);
GO
SELECT FirstName, LastName, Gender =
CASE GenderID
WHEN 1 THEN 'Male'
WHEN 2 THEN 'Female'
ELSE 'Unknown'
END
FROM PersonsDetail