Product Quantity Deduction On Condition - sql

I've a requirement to deduct product quantity on a condition. It seems little bit complicated and not sure how to do it using sql query. Here is the concept on it: Product means here raw material. For production purpose, we have to deduct raw materials from stock. There are few rules to follow:
Table - ProductEntry:
i) Products are purchased with PO (Purchase order) and invoice number from the supplier. There is a condition in this case. Suppose, 100 products for product id 1001 has been purchased and it came into two sections as follows:
Id - ProductId - PO - Invoice no - Quantity - Price - EntryDate
1st section: 1 - 1001 - PO-102 - Inv-122 - 20 - 200 - 2017-07-10 10:00:00
2nd section: 2 - 1001 - PO-102 - Inv-122 - 80 - 800 - 2017-07-10 11:00:00
3 - 1002 - PO-102 - Inv-122 - 20 - 400 - 2017-07-10 10:00:00
Here starts the game. In many cases, the raw material or product may come into multiple sections or fully at a time I mean suppose 100 pieces total.
ii) Now after it has been purchased, it has to get into the store and there is another procedure. Each purchased product should be received with a IP (import permit) number separately as follows:
Table - IpEntry:
Id - ProductId - Invoice no - IP - AnotherProductId
1 - 1001 - Inv-122 - IP2244 - 2
2 - 1001 - Inv-122 - IP2244 - 2
3 - 1002 - Inv-122 - IP2244 - 4
iii) After receiving the products, it should be used for production purpose, that means, there will be consumption. In consumption, the first entered product or raw material should be used. That means, if product id 1001 has to be deducted, then the first entered should be deducted based on 'EntryDate' as it has entered at the min. time 10:00:00 on the same date. So for deduction or consumption, following should take place:
Table - Consumption:
Id - Consumption no - AnotherProductId - Quantity
1 - Con-122 - 2 - 10
3 - Con-122 - 4 - 10
So the final output will be the following:
Id - AnotherProductId - Stock - Quantity Used - Remaining Balance
1 - 2 - 10 - 10 - 100
2 - 4 - 10 - 10 - 200
I am not sharing the sql query here as it is supposed to be not accurate and simple enough using INNER JOIN and MIN functions that returns the following:
Id - AnotherProductId - Stock - Quantity Used - Remaining Balance
1 - 2 - 10 - 10 - 100
2 - 2 - 10 - 10 - 100 //It returns **AnotherProductId or ProductId - 1001 or 2** twice as it should only return once
3 - 4 - 10 - 10 - 200
I am not sure how to do deal with the above scenario specifically same product with different quantity and little bit confused.
Here is the script for better understanding:
USE [Demo]
GO
/****** Object: Table [dbo].[ProductEntry] Script Date: 07/19/2017 20:37:41 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ProductEntry](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ProductId] [int] NULL,
[PO] [nvarchar](60) NULL,
[Invoice No] [nvarchar](60) NULL,
[Quantity] [float] NULL,
[Price] [float] NULL,
[EntryDate] [datetime] NULL,
CONSTRAINT [PK_ProductEntry] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[ProductEntry] ON
INSERT [dbo].[ProductEntry] ([Id], [ProductId], [PO], [Invoice No], [Quantity], [Price], [EntryDate]) VALUES (1, 1001, N'PO-102', N'Inv-122', 20, 200, CAST(0x0000A7AC00A4CB80 AS DateTime))
INSERT [dbo].[ProductEntry] ([Id], [ProductId], [PO], [Invoice No], [Quantity], [Price], [EntryDate]) VALUES (2, 1001, N'PO-102', N'Inv-122', 80, 800, CAST(0x0000A7AC00B54640 AS DateTime))
INSERT [dbo].[ProductEntry] ([Id], [ProductId], [PO], [Invoice No], [Quantity], [Price], [EntryDate]) VALUES (3, 1002, N'PO-102', N'Inv-122', 20, 400, CAST(0x0000A7AC00A4CB80 AS DateTime))
SET IDENTITY_INSERT [dbo].[ProductEntry] OFF
/****** Object: Table [dbo].[IpEntry] Script Date: 07/19/2017 20:37:41 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[IpEntry](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ProductId] [int] NULL,
[Invoice No] [nvarchar](60) NULL,
[IP] [nvarchar](60) NULL,
[AnotherProductId] [int] NULL,
CONSTRAINT [PK_IpEntry] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[IpEntry] ON
INSERT [dbo].[IpEntry] ([Id], [ProductId], [Invoice No], [IP], [AnotherProductId]) VALUES (1, 1001, N'Inv-122', N'IP2244', 2)
INSERT [dbo].[IpEntry] ([Id], [ProductId], [Invoice No], [IP], [AnotherProductId]) VALUES (2, 1001, N'Inv-122', N'IP2244', 2)
INSERT [dbo].[IpEntry] ([Id], [ProductId], [Invoice No], [IP], [AnotherProductId]) VALUES (3, 1002, N'Inv-122', N'IP2244', 4)
SET IDENTITY_INSERT [dbo].[IpEntry] OFF
/****** Object: Table [dbo].[Consumption] Script Date: 07/19/2017 20:37:41 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Consumption](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Consumption no] [nvarchar](40) NULL,
[AnotherProductId] [int] NULL,
[Quantity] [float] NULL,
CONSTRAINT [PK_Consumption] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Consumption] ON
INSERT [dbo].[Consumption] ([Id], [Consumption no], [AnotherProductId], [Quantity]) VALUES (1, N'Con-122 ', 2, 10)
INSERT [dbo].[Consumption] ([Id], [Consumption no], [AnotherProductId], [Quantity]) VALUES (2, N'Con-122 ', 4, 10)
SET IDENTITY_INSERT [dbo].[Consumption] OFF

This should give you expected result. Please try.
;WITH CTE AS (
select DISTINCT ProductID,AnotherProductId,Balance,
CASE WHEN Balance>=0 THEN 'P' ELSE 'N' END Flag, row_number() over(partition by AnotherProductId order by Balance) RID
FROM (SELECT DISTINCT P.ProductID,I.AnotherProductId,(P.Quantity-C.Quantity) 'Balance'
FROM [ProductEntry] P INNER JOIN [IpEntry] I ON I.ProductID=P.ProductId
INNER JOIN (SELECT [AnotherProductId],SUM([Quantity]) [Quantity] FROM [Consumption] GROUP BY [AnotherProductId]) C ON C.AnotherProductId=I.AnotherProductId
)A
)
select T.AnotherProductId,Balance as Stock, C.Quantity as 'Quantity Used',MIN((P.Price *(P.Quantity-C.Quantity)/P.Quantity)) 'Remaining Balance'
FROM [ProductEntry] P INNER JOIN CTE T ON T.ProductID=P.ProductId AND (RID=1 OR Flag='N')
INNER JOIN (SELECT DISTINCT ProductId,AnotherProductId FROM [IpEntry]) I ON I.ProductID=P.ProductId
INNER JOIN (SELECT [AnotherProductId],SUM([Quantity]) [Quantity] FROM [Consumption] GROUP BY [AnotherProductId]) C ON C.AnotherProductId=I.AnotherProductId
GROUP BY T.AnotherProductId,Balance, C.Quantity

This is expected to cover all the scenarios.
SELECT DISTINCT P.ProductID,P.Quantity,-1 Flag,C.[Quantity] Balance
INTO #TMP
FROM [ProductEntry] P
INNER JOIN [IpEntry] I ON I.ProductID=P.ProductId
INNER JOIN (SELECT [AnotherProductId],SUM([Quantity]) [Quantity] FROM [Consumption] GROUP BY [AnotherProductId])C ON C.AnotherProductId=I.AnotherProductId
DECLARE #Counter INT=1
WHILE((SELECT TOP 1 1 FROM #TMP WHERE Flag=-1 )=1)
BEGIN
UPDATE T SET T.Balance = T.Balance-T.Quantity,
T.Quantity = CASE WHEN T.Quantity-T.Balance>=0 THEN T.Quantity-T.Balance ELSE 0 END,
T.Flag = CASE WHEN T.Quantity-T.Balance>=0 THEN 0 ELSE 1 END
FROM (SELECT ProductId,Quantity,row_number() over (partition by ProductId order by Quantity)RID FROM [ProductEntry])P
INNER JOIN [IpEntry] I ON I.ProductID=P.ProductId and P.RID=#Counter
INNER JOIN (SELECT ProductId,Quantity,Flag,Balance,row_number() over (partition by ProductId order by Quantity)RID FROM #TMP ) T ON T.ProductID=P.ProductID and T.RID=#Counter
INNER JOIN (SELECT [AnotherProductId],SUM([Quantity]) [Quantity] FROM [Consumption] GROUP BY [AnotherProductId])C ON C.AnotherProductId=I.AnotherProductId
UPDATE T1 SET Balance=T2.Balance
FROM #TMP T1 INNER JOIN #TMP T2 ON T1.ProductId=T2.ProductId
WHERE T2.Flag IN (0,1)
UPDATE T1 SET Flag= (SELECT T2.Flag FROM #TMP T2 WHERE T1.ProductId=T2.ProductId AND T2.Flag=0)
FROM #TMP T1
WHERE Flag=0
SET #Counter=#Counter+1
SELECT * FROM #TMP
END
SELECT ProductId,Quantity FROM #TMP --You can add more details by joining with other tables as per your requirement
drop table #TMP

Related

Write SQL to identify multiple subgroupings within a grouping

I have a program that summarizes non-normalized data in one table and moves it to another and we frequently get a duplicate key violation on the insert due to bad data. I want to create a report for the users to help them identify the cause of the error.
For example, consider the following contrived simple SQL which summarizes data in the table Companies and inserts it into CompanySum, which has a primary key of State/Zone. In order for the INSERT not to fail, there cannot be more than one distinct combinations of Company/Code for every unique primary key State/Zone combination. If there is, we want the insert to fail so that the data can be corrected.
INSERT INTO CompanySum
(
[State]
,[Zone]
,[Company]
,[Code]
,[Revenue]
)
SELECT
--Keys of target
[State]
,[Zone]
--We are expecting to have one distinct combination of these fields per key grouping
,[Company]
,[Code]
--Aggregate
,SUM([Revenue])
FROM COMPANIES
GROUP BY
[State]
,[Zone]
,[Company]
,[Code]
I would like to create a report to help the users easily identify and correct the data so that there is only one distinct Company/Code combination within a State/Zone. For each distinct State/Zone value, I would like to identify the distinct Company/Code combinations within the State/Zone. If there are more than one Company/Code combinations within a State/Zone, I would like all of the records in the State/Zone to be displayed in the output. For example, here is the sample input and desired output:
Data:
RecordNumber State Zone Company Code Revenue
------------ ----- ---- ------- ---- --------
1 CT B State of CT 65453 10
2 CT B State of CT 65453 3
3 CT B Travelers 33443 20
4 CT C Cigna 45678 24
5 CT C Cigna 45678 234
6 MI A GM 48089 100
7 MI A GM 54555 200
8 MI B Chrysler 43434 44
Desired Output:
RecordNumber State Zone Company Code Revenue
------------ ----- ---- ------- ---- --------
1 CT B State of CT 65453 10
2 CT B State of CT 65453 3
3 CT B Travelers 33443 20
6 MI A GM 48089 100
7 MI A GM 54555 200
Here is the DDL and DML needed to create this test scenario
CREATE TABLE [dbo].[Companies](
[RecordNumber] [int] NULL,
[State] [char](2) NOT NULL,
[Zone] [varchar](30) NOT NULL,
[Company] [varchar](30) NOT NULL,
[Code] [varchar](30) NOT NULL,
[Revenue] [numeric](9, 1) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[CompanySum](
[State] [char](2) NOT NULL,
[Zone] [varchar](30) NOT NULL,
[Company] [varchar](30) NOT NULL,
[Code] [varchar](30) NOT NULL,
[Revenue] [numeric](9, 1) NULL,
CONSTRAINT [PK_CompanySum] PRIMARY KEY CLUSTERED
(
[State] ASC,
[Zone] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DELETE FROM [dbo].[Companies]
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (1, N'CT', N'B', N'State of CT', N'65453', CAST(10.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (2, N'CT', N'B', N'State of CT', N'65453', CAST(3.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (3, N'CT', N'B', N'Travelers', N'33443', CAST(20.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (4, N'CT', N'C', N'Cigna', N'45678', CAST(24.0 AS Numeric(9, 1)))
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (5, N'CT', N'C', N'Cigna', N'45678', CAST(234.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (6, N'MI', N'A', N'GM', N'48089', CAST(100.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (7, N'MI', N'A', N'GM', N'54555', CAST(200.0 AS Numeric(9, 1)))
GO
INSERT [dbo].[Companies] ([RecordNumber], [State], [Zone], [Company], [Code], [Revenue]) VALUES (8, N'MI', N'B', N'Chrysler', N'43434', CAST(44.0 AS Numeric(9, 1)))
GO
This is a hopefully better re-construction of a previous post of mine SQL to return unique combinations of non key columns within a set of key columns where I am trying to help clarify the question and provide a simple working example that readers can use.
Please see this SQL Fiddle:
http://sqlfiddle.com/#!18/d0141/1
Is this a solution?
Fiddle: http://sqlfiddle.com/#!18/12e9a0/9
select c.*
from
Companies c
inner join (
select State, Zone
from Companies
group by State, Zone
having count(distinct Company + Code) > 1
) as dup_state_zone
on(
c.State = dup_state_zone.State
and c.Zone = dup_state_zone.Zone
)
Edited - Fix the having clause, with a little cheat...
I used windows ranking function to rank the records by state ordering by zone ascending, to get the desired output.
Suggestion: I would like to say that the insert statement of your CompanySum will ail due to your primary key constraint as you select duplicate key records. in this case you need to change your primary key constraint a little.
CONSTRAINT [PK_CompanySum] PRIMARY KEY CLUSTERED
(
[State] ASC,
[Zone] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Since State and zone both are with duplicate values this insert will fail. better add a auto increment primary key, or include RecordNumber in to Primary key constraint rather than using State and Zone to make it usnique as there are duplicate values in your desired output.
SELECT
A.[RecordNumber]
,A.[State]
,A.[Zone]
,A.[Company]
,A.Code
,A.Revenue
FROM
(
SELECT *
,RANK() OVER (PARTITION BY [STATE] ORDER BY Zone) AS [row]
FROM Companies
) AS A
WHERE [row] =1
Highlighted are duplicates which will make your insert fail.

SQL server query, sort on multiple columns

We have a nested structure of tasks in which every task can contain other tasks. Order of tasks in a task is important and is defined by the Sequence field starting at zero.
Here is my table structure:
USE [MyDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Relation](
[PK_ID] [int] IDENTITY(1,1) NOT NULL,
[SourceEntityId] [uniqueidentifier] NOT NULL,
[TargetEntityId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Relation] PRIMARY KEY CLUSTERED
(
[PK_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TaskTable1](
[Id] [uniqueidentifier] NOT NULL,
[Title] [nvarchar](max) NULL,
[SequenceId] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TaskTable2](
[Id] [uniqueidentifier] NOT NULL,
[Title] [nvarchar](max) NULL,
[SequenceId] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Relation] ON
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (1, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'5b266fd1-cbc8-c16a-91c4-5675a35c9ecf')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (2, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'e499ca68-8103-b8ec-06ba-110fa3f6eb5b')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (4, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'645ad2eb-df10-0d5b-0526-408aad45a145')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (5, N'785227d1-393c-ae18-02e5-03ab08d577af', N'5655aeb7-b8b5-dca9-38af-37687c668c14')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (6, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'030cdefc-0e45-01e6-e2a5-a69e303bda4b')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (7, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'0375c7a1-8cc5-a4c8-151c-966e4af83f73')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (8, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'785227d1-393c-ae18-02e5-03ab08d577af')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (9, N'030cdefc-0e45-01e6-e2a5-a69e303bda4b', N'8324bba9-252f-bef8-c018-8b86491e2361')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (10, N'030cdefc-0e45-01e6-e2a5-a69e303bda4b', N'f1cbe8a3-3285-4cf0-096d-aad0327bdb0b')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (11, N'dab00c89-961c-84dd-bb43-cffd18e63594', N'0189f0af-5045-a498-2d70-99187bf3f0ae')
GO
INSERT [dbo].[Relation] ([PK_ID], [SourceEntityId], [TargetEntityId]) VALUES (12, N'785227d1-393c-ae18-02e5-03ab08d577af', N'ffecd091-c17b-ee5f-a64d-54ea9ff65aa9')
GO
SET IDENTITY_INSERT [dbo].[Relation] OFF
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'5b266fd1-cbc8-c16a-91c4-5675a35c9ecf', N'First', 0)
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'e499ca68-8103-b8ec-06ba-110fa3f6eb5b', N'Second', 1)
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'0189f0af-5045-a498-2d70-99187bf3f0ae', N'Fourth', 3)
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'0375c7a1-8cc5-a4c8-151c-966e4af83f73', N'Sixth', 5)
GO
INSERT [dbo].[TaskTable2] ([Id], [Title], [SequenceId]) VALUES (N'030cdefc-0e45-01e6-e2a5-a69e303bda4b', N'Fifth', 4)
GO
INSERT [dbo].[TaskTable2] ([Id], [Title], [SequenceId]) VALUES (N'785227d1-393c-ae18-02e5-03ab08d577af', N'Seventh', 6)
GO
INSERT [dbo].[TaskTable2] ([Id], [Title], [SequenceId]) VALUES (N'645ad2eb-df10-0d5b-0526-408aad45a145', N'Third', 2)
GO
INSERT [dbo].[TaskTable2] ([Id], [Title], [SequenceId]) VALUES (N'8324bba9-252f-bef8-c018-8b86491e2361', N'sub1', 0)
GO
INSERT [dbo].[TaskTable2] ([Id], [Title], [SequenceId]) VALUES (N'f1cbe8a3-3285-4cf0-096d-aad0327bdb0b', N'sub2', 1)
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'ffecd091-c17b-ee5f-a64d-54ea9ff65aa9', N'sub 1', 0)
GO
INSERT [dbo].[TaskTable1] ([Id], [Title], [SequenceId]) VALUES (N'5655aeb7-b8b5-dca9-38af-37687c668c14', N'sub 2', 1)
GO
To get the tasks in order with their child tasks right beneath their parents, I tried the following query to no avail:
;With TaskCTE
AS
(
select R.SourceEntityId AS ParentTask_Id, R.TargetEntityId AS Task_Id , cast(null as uniqueidentifier) AS ParentTask, 0 AS Level
, ROW_NUMBER() OVER (ORDER BY (SELECT 100)) / power(10.0,0) as x
from Relation R
where (R.SourceEntityId = 'DAB00C89-961C-84DD-BB43-CFFD18E63594')
UNION ALL
select R1.SourceEntityId , R1.TargetEntityId, TaskCTE.Task_Id , Level + 1
, x + ROW_NUMBER() OVER (ORDER BY (SELECT 100)) / power(10.0,level+1)
from Relation R1
INNER JOIN TaskCTE
ON R1.SourceEntityId = TaskCTE.Task_Id
)
select ParentTask_Id, Task_Id, ParentTask, Level
, COALESCE(TT1.Title, TT2.Title) AS Title
, COALESCE(TT1.SequenceId, TT2.SequenceId) AS SequenceId
, x
from TaskCTE
LEFT OUTER JOIN TaskTable1 TT1
ON TaskCTE.Task_Id = TT1.Id
LEFT OUTER JOIN TaskTable2 TT2
ON TaskCTE.Task_Id = TT2.Id
order by level , SequenceId
If you follow the structure of required output (shown in below image), the sequence ** column along with the **Level column must determine the sort order.
Thanks in advance
Edit: My query output which is wrong:
If your problem is that sequence field in other table rather than relation table, then why do not you join them before running recursion? But it likely will be slower than your initial query. Here's a sample
with cte as (
select
r.SourceEntityId, r.TargetEntityId, t.SequenceId, 0 k
from
Relation r
join (
select * from TaskTable1
union all
select * from TaskTable2
) t on r.TargetEntityId = t.id
---------------------------------------
union all select * from cte where k = 1
---------------------------------------
)
, rcte as (
select
SourceEntityId, TargetEntityId, ParentTask = cast(null as uniqueidentifier)
, SequenceId, rn = cast(row_number() over (order by SequenceId) as varchar(8000)), 1 step
from
cte
where
SourceEntityId = 'DAB00C89-961C-84DD-BB43-CFFD18E63594'
union all
select
a.TargetEntityId, b.TargetEntityId, a.SourceEntityId, b.SequenceId
, cast(concat(a.rn, '.', row_number() over (partition by b.SourceEntityId order by b.SequenceId)) as varchar(8000))
, step + 1
from
rcte a
join cte b on a.TargetEntityId = b.SourceEntityId
)
select
*
from
rcte
order by rn
I have not included your X column, I can not get what are trying to calculate. Also, in your expected output values of ParentTask and ParentTask_Id are same. Should be so?
I am using same query as #Uzi with minor correction.I am having same doubts as him.#Yasser should clearly show what output is desire in proper output and remove unnecessary columns.
if row_number only purpose is to order record then why convert it to varchar(8000).Also you can avoid expensive Row_number all together.
Take advantage of PK_ID instead of expensive row_number,even if PK_ID is not in sequence in this case.
If performance is big issue then user should mention number of rows in 3 TABLE AND WHAT OTHER FILTER be applied IN WHERE CONDITION ?
Why data type is uniqueidentifier ?Will it solve the purpose if it is INT?
Read this
Check this query,
WITH cte
AS (
SELECT r.PK_ID
,r.SourceEntityId
,r.TargetEntityId
,t.SequenceId,0 k
FROM #Relation r
JOIN (
SELECT id
,SequenceId
FROM #TaskTable1
UNION ALL
SELECT id
,SequenceId
FROM #TaskTable2
) t ON r.TargetEntityId = t.id
---------------------------------------
--union all select * from cte where k = 1
---------------------------------------
)
,rcte
AS (
SELECT SourceEntityId
,TargetEntityId
,ParentTask = cast(NULL AS UNIQUEIDENTIFIER)
,SequenceId
, rn = cast(row_number() over (order by SequenceId) as decimal(3,1))
--, rn = cast( SequenceId+1 as decimal(3,1))--**
,1 step
FROM cte
WHERE SourceEntityId = 'DAB00C89-961C-84DD-BB43-CFFD18E63594'
UNION ALL
SELECT a.TargetEntityId
,b.TargetEntityId
,a.SourceEntityId
,b.SequenceId
,cast((a.rn+(b.SequenceId/10.0)) as decimal(3,1))
,step + 1
FROM rcte a
JOIN cte b ON a.TargetEntityId = b.SourceEntityId
)
SELECT *
FROM rcte
ORDER BY rn
--**
--SELECT *
--FROM rcte
--ORDER BY rn,st
-- 2nd Edit,
I understand that there is no way of changing database.
In that case it is very logical to create index view where task table id is Clustered index.
select id, SequenceId from #TaskTable1
union all
select id, SequenceId from #TaskTable2
Create nonclustered index NCI_Relation_SourceID on Relation([SourceEntityId])
Create nonclustered index NCI_Relation_TargetEntityId on Relation([TargetEntityId])
you can once try this combination,
Remove PK_ID as clustered index and make TargetEntityId as clustered index.
you can once try creating view on this query,
SELECT r.PK_ID
,r.SourceEntityId
,r.TargetEntityId
,t.SequenceId
FROM #Relation r
JOIN (
SELECT id
,SequenceId
FROM #TaskTable1
UNION ALL
SELECT id
,SequenceId
FROM #TaskTable2
) t ON r.TargetEntityId = t.id
by adding a new column named Hierarchy in CTE expression and sorting outcome according to this value could solve your requirement
Here is the modified CTE query
;With TaskCTE AS
(
select
R.SourceEntityId AS ParentTask_Id,
R.TargetEntityId AS Task_Id , cast(null as uniqueidentifier) AS ParentTask, 0 AS Level
, ROW_NUMBER() OVER (ORDER BY (SELECT 100)) / power(10.0,0) as x
,CAST( ROW_NUMBER() OVER (ORDER BY R.SourceEntityId) as varchar(max)) Hierarchy
from Relation R
where (R.SourceEntityId = 'DAB00C89-961C-84DD-BB43-CFFD18E63594')
UNION ALL
select R1.SourceEntityId , R1.TargetEntityId, TaskCTE.Task_Id , Level + 1
, x + ROW_NUMBER() OVER (ORDER BY (SELECT 100)) / power(10.0,level+1)
,CAST(Hierarchy + ':' + CAST(ROW_NUMBER() OVER (ORDER BY R1.SourceEntityId) as varchar(max)) as varchar(max)) as Hierarchy
from Relation R1
INNER JOIN TaskCTE
ON R1.SourceEntityId = TaskCTE.Task_Id
)
select ParentTask_Id, Task_Id, ParentTask, Level
, COALESCE(TT1.Title, TT2.Title) AS Title
, COALESCE(TT1.SequenceId, TT2.SequenceId) AS SequenceId
, x
,Hierarchy
from TaskCTE
LEFT OUTER JOIN TaskTable1 TT1
ON TaskCTE.Task_Id = TT1.Id
LEFT OUTER JOIN TaskTable2 TT2
ON TaskCTE.Task_Id = TT2.Id
order by Hierarchy
Please note that I have added Hierarchy column and its value is calculated using a ROW_NUMBER() function which creates a unique integer value for each task
You can find implemantation of this hierarchy query with SQL CTE at refereced tutorial
I hope it helps
I am also adding the output as screenshot here to show how data is sorted according to Hierarchy
Although childs are listed after parents, it does not one-to-one match with your desired outcome as I could see

T-SQL: Reduce query load when getting multilevel product categories

I'm storing the categories to whichs products in [products] are assigned in [products_category_mapping].
A product can be assigned to a category in level 0, 1 and 2. So a product will occur in [products_category_mapping] either 1, 2 or 3 times depending on to which levels the product is assigned.
Then I want to retrieve via a query the data on the categories to which those products belong.
These queries are quite expensive since they contain a lot of joins and we need to check for each category level whether the product occurs for that level. Also there around 200.000 products, each assigned to 1,2 or 3 categories, so it takes a very long time to run the queries below.
My question: how can I optimize the performance of these queries?
ps. please refrain from suggesting a table redesign, seeing where we are in the process, that is not viable right now.
Current queries:
SELECT label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
,description_nl as description_nl_0,description_en as description_en_0
,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=0
SELECT label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
,description_nl as description_nl_0,description_en as description_en_0
,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=1
SELECT label_nl+';'+slug_nl as labelslug_nl_2,label_en+';'+slug_en as labelslug_en_2,label_nl as label_nl_2,label_en as label_en_2,slug_nl as slug_nl_2,slug_en as slug_en_2
,pagetitle_nl as pagetitle_nl_2,pagetitle_en as pagetitle_en_2,image_nl as image_nl_2,image_en as image_en_2
,description_nl as description_nl_2,description_en as description_en_2
,metadescription_nl as metadescription_nl_2,metadescription_en as metadescription_en_2
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=2
DDL
USE [mydb]
GO
/****** Object: Table [dbo].[articlegroups] Script Date: 29-04-2017 18:34:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[articlegroups](
[id] [int] IDENTITY(1,1) NOT NULL,
[parentid] [int] NOT NULL,
[catlevel] [tinyint] NOT NULL,
[label_nl] [nvarchar](50) NOT NULL,
[label_en] [nvarchar](50) NOT NULL,
[slug_nl] [nvarchar](50) NOT NULL,
[slug_en] [nvarchar](50) NOT NULL,
[pagetitle_nl] [nvarchar](100) NULL,
[pagetitle_en] [nvarchar](100) NULL,
[image_nl] [nvarchar](50) NULL,
[image_en] [nvarchar](50) NULL,
[description_nl] [nvarchar](500) NOT NULL,
[description_en] [nvarchar](500) NULL,
[metadescription_nl] [nvarchar](200) NULL,
[metadescription_en] [nvarchar](200) NULL,
[createdate] [datetime] NOT NULL,
[canonicalurl_nl] [nvarchar](150) NULL,
[canonicalurl_en] [nvarchar](150) NULL,
CONSTRAINT [PK_articlegroups] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[articlegroups] ADD CONSTRAINT [DF_articlegroups_lvl0_catlevel] DEFAULT ((0)) FOR [catlevel]
GO
ALTER TABLE [dbo].[articlegroups] ADD CONSTRAINT [DF_articlegroups_createdate] DEFAULT (getdate()) FOR [createdate]
GO
USE [mydb]
GO
/****** Object: Table [dbo].[products_category_mapping] Script Date: 29-04-2017 18:33:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[products_category_mapping](
[artikelid] [int] NOT NULL,
[articlegroup_id] [int] NOT NULL,
[createdate] [datetime] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[products_category_mapping] ADD CONSTRAINT [DF_products_category_mapping_createdate] DEFAULT (getdate()) FOR [createdate]
GO
ALTER TABLE [dbo].[products_category_mapping] WITH CHECK ADD CONSTRAINT [FK_articlegroups_lvl1_mapping_products] FOREIGN KEY([artikelid])
REFERENCES [dbo].[products] ([id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[products_category_mapping] CHECK CONSTRAINT [FK_articlegroups_lvl1_mapping_products]
GO
ALTER TABLE [dbo].[products_category_mapping] WITH CHECK ADD CONSTRAINT [FK_products_category_mapping_articlegroups] FOREIGN KEY([articlegroup_id])
REFERENCES [dbo].[articlegroups] ([id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[products_category_mapping] CHECK CONSTRAINT [FK_products_category_mapping_articlegroups]
GO
USE [mydb]
GO
/****** Object: Table [dbo].[products] Script Date: 29-04-2017 18:33:12 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[products](
[id] [int] IDENTITY(1,1) NOT NULL,
[friendlyurl] [nvarchar](200) NULL,
CONSTRAINT [PK_products] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [mydb]
GO
SET IDENTITY_INSERT [dbo].[articlegroups] ON
GO
INSERT [dbo].[articlegroups] ([id], [parentid], [catlevel], [label_nl], [label_en], [slug_nl], [slug_en], [pagetitle_nl], [pagetitle_en], [image_nl], [image_en], [description_nl], [description_en], [metadescription_nl], [metadescription_en], [createdate], [canonicalurl_nl], [canonicalurl_en]) VALUES (129, 0, 0, N'Baby / Geboorte', N'Baby and birth', N'baby-en-geboorte', N'baby-and-birth', N'Baby- en geboorte producten online kopen', N'Baby and birth', N'', N'', N'Alle baby en geboorte artikelen in 1 overzicht. Van kinderwagens tot baby-monitors tot commodes tot de inrichting van de kraamkamer.', N'', N'Online baby en geboorte producten kopen kan hier. Goedkope producten die jou helpen wanneer je net een baby hebt!', N'', CAST(N'2016-04-12T23:31:43.003' AS DateTime), NULL, NULL)
GO
INSERT [dbo].[articlegroups] ([id], [parentid], [catlevel], [label_nl], [label_en], [slug_nl], [slug_en], [pagetitle_nl], [pagetitle_en], [image_nl], [image_en], [description_nl], [description_en], [metadescription_nl], [metadescription_en], [createdate], [canonicalurl_nl], [canonicalurl_en]) VALUES (680, 129, 1, N'Geboortekaartjes', N'Birth announcement', N'geboorte-kaartjes', N'birth-announcement-cards', N'Baby & Geboortekaartjes bestellen, drukken en versturen', N'Birth Announcements & Baby Birth Announcement Cards', N'', N'', N'Baby & geboortekaartjes bestel je in onze webshop. Wij hebben een flink aanbod in mooie en leuke kaartjes voor baby en geboorte. Schattige kaartjes bestellen.', N'', N'Leuke kaartjes voor baby & geboorte kun je hier eenvoudig bestellen. Originele en leuke kaarten speciaal voor de kleintjes.', N'', CAST(N'2016-04-12T23:31:43.310' AS DateTime), NULL, NULL)
GO
SET IDENTITY_INSERT [dbo].[articlegroups] OFF
GO
SET IDENTITY_INSERT [dbo].[products] ON
GO
INSERT [dbo].[products] ([id],[friendlyurl]) VALUES (3216743, N'birth-with-flowers')
INSERT [dbo].[products_category_mapping] ([artikelid], [articlegroup_id], [createdate]) VALUES (3216743, 129, CAST(N'2017-04-24T20:05:58.463' AS DateTime))
GO
INSERT [dbo].[products_category_mapping] ([artikelid], [articlegroup_id], [createdate]) VALUES (3216743, 680, CAST(N'2017-04-24T20:05:58.463' AS DateTime))
GO
** UPDATE 1 **
As per suggestion of #HABO below in comments, I tried using a case statement to combine the 3 queries:
SELECT
CASE
when ga.catlevel=0 THEN
label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
,description_nl as description_nl_0,description_en as description_en_0
,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0
when ga.catlevel=1 THEN
label_nl+';'+slug_nl as labelslug_nl_1,label_en+';'+slug_en as labelslug_en_1,label_nl as label_nl_1,label_en as label_en_1,slug_nl as slug_nl_1,slug_en as slug_en_1
,pagetitle_nl as pagetitle_nl_1,pagetitle_en as pagetitle_en_1,image_nl as image_nl_1,image_en as image_en_1
,description_nl as description_nl_1,description_en as description_en_1
,metadescription_nl as metadescription_nl_1,metadescription_en as metadescription_en_1
else null
END
FROM globos_articlegroups ga WITH (NOLOCK)
INNER JOIN globos_products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN globos_products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel in (0,1,2)
It's however not clear to me yet, how I can make sure how I can differentiate the columns based on the cat levels: 0,1 or 2
UPDATE 2
I get the desired resultset, but it has multiple rows that I want to merge into a single row. When there are multiple rows, each column always has a maximim of 1 value with the other values being NULL. I want to merge the multiple rows into a single row where the highest value per column (i.e. not NULL) will remain.
Current resultset
<table>
<tbody>
<tr>
<th>labelslug_nl_0
</th>
<th>labelslug_nl_1
</th>
<th>labelslug_nl_2
</th>
</tr>
<tr>
<td>Baby / Geboorte;baby-en-geboorte
</td>
<td>NULL
</td>
<td>NULL
</td>
</tr>
<tr>
<td>NULL
</td>
<td>Geboortekaartjes;geboorte-kaartjes
</td>
<td>NULL
</td>
</tr>
</tbody>
</table>
Desired resultset
<table>
<tbody>
<tr>
<th>labelslug_nl_0
</th>
<th>labelslug_nl_1
</th>
<th>labelslug_nl_2
</th>
</tr>
<tr>
<td>Baby / Geboorte;baby-en-geboorte
</td>
<td>Geboortekaartjes;geboorte-kaartjes
</td>
<td>NULL
</td>
</tr>
</tbody>
</table>
This single query will retrieve all of the rows that your three queries returned. CatLevel 0 and 1 rows will populate the first set of columns and leave the second set filled with nulls. CatLevel 2 rows will do the opposite.
select
case when AG.CatLevel in ( 0, 1 ) then label_nl + ';' + slug_nl end as labelslug_nl_0,
case when AG.CatLevel in ( 0, 1 ) then label_en + ';' + slug_en as labelslug_en_0,
case when AG.CatLevel in ( 0, 1 ) then label_nl end as label_nl_0,
case when AG.CatLevel in ( 0, 1 ) then label_en end as label_en_0,
case when AG.CatLevel in ( 0, 1 ) then slug_nl end as slug_nl_0,
case when AG.CatLevel in ( 0, 1 ) then slug_en end as slug_en_0,
case when AG.CatLevel in ( 0, 1 ) then pagetitle_nl end as pagetitle_nl_0,
case when AG.CatLevel in ( 0, 1 ) then pagetitle_en end as pagetitle_en_0,
case when AG.CatLevel in ( 0, 1 ) then image_nl end as image_nl_0,
case when AG.CatLevel in ( 0, 1 ) then image_en end as image_en_0,
case when AG.CatLevel in ( 0, 1 ) then description_nl end as description_nl_0,
case when AG.CatLevel in ( 0, 1 ) then description_en end as description_en_0,
case when AG.CatLevel in ( 0, 1 ) then metadescription_nl end as metadescription_nl_0,
case when AG.CatLevel in ( 0, 1 ) then metadescription_en end as metadescription_en_0,
case when AG.CatLevel = 2 then label_nl + ';' + slug_nl end as labelslug_nl_2,
case when AG.CatLevel = 2 then label_en + ';' + slug_en as labelslug_en_2,
case when AG.CatLevel = 2 then label_nl end as label_nl_2,
case when AG.CatLevel = 2 then label_en end as label_en_2,
case when AG.CatLevel = 2 then slug_nl end as slug_nl_2,
case when AG.CatLevel = 2 then slug_en end as slug_en_2,
case when AG.CatLevel = 2 then pagetitle_nl end as pagetitle_nl_2,
case when AG.CatLevel = 2 then pagetitle_en end as pagetitle_en_2,
case when AG.CatLevel = 2 then image_nl end as image_nl_2,
case when AG.CatLevel = 2 then image_en end as image_en_2,
case when AG.CatLevel = 2 then description_nl end as description_nl_2,
case when AG.CatLevel = 2 then description_en end as description_en_2,
case when AG.CatLevel = 2 then metadescription_nl end as metadescription_nl_2,
case when AG.CatLevel = 2 then metadescription_en end as metadescription_en_2
from ArticleGroups as AG inner join
Products_Category_Mapping as PCM on PCM.ArticleGroup_Id = AG.Id inner join
Products as P on P.Id = PCM.ArtikelId
where P.Id = 3216743 and AG.CatLevel in ( 0, 1, 2 )
Note that all of the columns referenced should include aliases, e.g. AG.Label_NL. This is left as an exercise to the OP.

How can I display repeated values only one time and have '-' if it repeats

I have some subqueries that retreives the same values for each PolicyNumber. How can I substitute repeated value with '-' and only display it one in a top row for each policy?
Right now I have this:
But I need something like this:
SELECT
-------------/* GrossPremium*/
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
AS GrossPremium
--------------/*CompanyCommissionPercentage*/
,((SELECT ISNULL(SUM(tblFin_InvoiceDetails.MGAAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
+
CASE WHEN INV.Remitter = 'B' then
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.RemitterAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))----------------RemitterCommission
ELSE
(SELECT ISNULL(SUM(tblFin_InvoicedItemsPayees.PayeeAmt), 0)
FROM tblFin_InvoicedItemsPayees
INNER JOIN tblFin_PolicyCharges pc on pc.ChargeCode = tblFin_InvoicedItemsPayees.ChargeCode and pc.chargeType = 'P'
WHERE (tblFin_InvoicedItemsPayees.InvoiceNum = INV.InvoiceNum and tblFin_InvoicedItemsPayees.PayeeGuid = INV.ProducerLocationGuid))
END) * 100 /
NULLIF((SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum)),0)
AS CompanyCommissionPercentage
FROM [tblFin_PayablesWorking] PW
INNER JOIN tblFin_Invoices INV ON PW.InvoiceNumber=INV.InvoiceNum
Well since you do not mention you full query and Table schema, i will give your answer with two simple example.If you want replace your repeated value with - the follow this query(please change the columns name according to your needs).
IF YOU WANT TO SELECT YOUR EXISTING TABLE:
;with ts as (
select S1.[ProductID], row_number() over (partition by S1.[ProductID] order by S1.[ProductID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery and change the column accordingly
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
FROM ts
FOR USING SUBQUERY:
--CREATE TABLE [dbo].[Sales](
-- [SalesID] [uniqueidentifier] NOT NULL DEFAULT (newid()),
-- [ProductID] [int] NOT NULL,
-- [EmployeeID] [int] NOT NULL,
-- [Quantity] [smallint] NOT NULL,
-- [SaleDate] [datetime] NOT NULL CONSTRAINT [DF_SaleDate] DEFAULT (getdate()),
-- CONSTRAINT [PK_SalesID] PRIMARY KEY CLUSTERED
--(
-- [SalesID] ASC
--)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
--) ON [PRIMARY]
--GO
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'9498d566-e31b-4ac8-ab54-1c898471fba8', 2, 1, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'69c7dff4-fbac-48d3-ae0a-5027c816acd2', 2, 2, 2, CAST(N'2012-04-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'a40b9505-4a2c-4186-a89b-88a401248a58', 1, 1, 4, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'04856027-d7ad-40fe-889b-8d933595ffde', 3, 1, 2, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'173be2de-3b80-4a3d-8bcc-a74d0d70b3a9', 3, 2, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--GO
;with ts as (
SELECT
JOIN1.[SalesID] AS [SalesID]
, JOIN1.[ProductID]
, JOIN1.seqnum AS seqnum
, JOIN2.[EmployeeID], JOIN2.seqnum2 AS seqnum2
FROM
(
select row_number() over (order by S1.[SalesID] asc) as RowNumber
, S1.[SalesID] AS [SalesID]
, S1.[ProductID] AS [ProductID]
, row_number() over (partition by S1.[ProductID] order by S1.[SalesID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery ( For Example GrossPremium) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN1
INNER JOIN
(
select row_number() over (order by S2.[SalesID] asc) as RowNumber
, S2.[SalesID] AS [SalesID]
, S2.[EmployeeID] AS [EmployeeID]
, row_number() over (partition by S2.[EmployeeID] order by S2.[SalesID]) as seqnum2
from (SELECT [SalesID],[EmployeeID] FROM [Sales]) AS S2 --Replace 'SELECT [[SalesID]] FROM [Sales]' with your Subquery ( For Example CompanyCommissionPercentage) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN2
ON JOIN1.[SalesID]=JOIN2.[SalesID]
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
,(case when seqnum2 = 1 then [EmployeeID] ELSE '-' end) as [EmployeeID]
FROM (Select TOP 10000000 *FROM ts ORDER BY [SalesID] ASC ) AS ts -- Mentioning TOP is Must, or it will give Error
I do know why you are using - instead of NULL, - will take space
if you wanted to do it in SQL (you probably shouldn't it's pretty ugly) you could do something like this using LAG(). It relies on having a field that you can use to sort the records for each Policy Number, in my dummy data below I included a field called RecordID to do this.
SELECT
PolicyNumber
,CASE
WHEN LAG(GrossPremium) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(GrossPremium AS VARCHAR(MAX))
ELSE '-'
END GrossPremium
,CASE
WHEN LAG(CompanyComissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(CompanyComissionPercentage AS VARCHAR(MAX))
ELSE '-'
END CompanyComissionPercentage
,CASE
WHEN LAG(RemitterCommissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommissionPercentage AS VARCHAR(MAX))
ELSE '-'
END RemitterCommissionPercentage
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
FROM
(
-- Dummy data
SELECT
1234 PolicyNumber -- Partition the LAG() on the policy number.
,1 RecordID -- use this to order the LAG() function.
,8749.00 GrossPremium
,18 CompanyComissionPercentage
,10 RemitterCommissionPercentage
,874.90 RemitterCommission
,1574.82 GrossCommission
UNION ALL
SELECT
1234
,2 RecordID
,8749.00
,18
,10
,874.90
,1574.82
UNION ALL
SELECT
5678
,1 RecordID
,8749.00
,18
,10
,874.90
,1574.82
) x;

SQL Server 2005 Update Triggers not working for dependent columns

I have encountered a strange situation where the update trigger on a table is not updating columns that are dependent on other columns which are also getting updated during the update. Here is the background and the code for replicating this problem.
I have a commodities management application that keeps track of fruit prices everyday. I have a need to calculate the Price and Volume trend for fruits on a daily purpose. The daily Fruit prices and price volume calculations are stored in the FruitTrades table. I have defined an Update trigger on this table which will calculate the Price and Volume trend whenever a row is inserted or updated in this table.
The daily fruit prices and volume come to me in a flat file which I import into a simple Table called PriceData. Then I move the Price and Volume information from this table to the FruitTrades table using a simple INSERT statement. This fires the update triggers in the FruitTrades, but two of the columns do not get updated by the trigger. Any idea why?
Steps for replicating this problem are as follows:
-- STEP 1 (create the FruitTrades table)
CREATE TABLE [dbo].[FruitTrades](
[FID] [nchar](3) NOT NULL,
[TradeDate] [smalldatetime] NOT NULL,
[TAID] [tinyint] NULL,
[Price] [real] NOT NULL,
[Vol] [int] NULL,
[3DAvgPrice] [real] NULL,
[5DAvgPrice] [real] NULL,
[VolTrend] [real] NULL,
[VolTrendPrevD] [real] NULL,
CONSTRAINT [PK_FruitTrades] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
-- STEP 2 (Create the Update trigger)
CREATE TRIGGER [dbo].[TRG_FruitTrades_Analysis]
ON [dbo].[FruitTrades]
AFTER INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE FruitTrades SET
-- Calculate the 3 day average price
[FruitTrades].[3DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 3 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last3Trades
),
-- Calculate the 5 day average price
[FruitTrades].[5DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 5 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last5Trades
),
-- Fetch the previous days VolTrend and update VolTrendPrev column
[FruitTrades].[VolTrendPrevD] =
(
SELECT TOP 1 VolTrend FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate]
),
-- Calculate Volume Trend and update VolTrend column
[FruitTrades].[VolTrend] =
(
ISNULL([FruitTrades].[VolTrendPrevD], 0) +
([Inserted].[Vol] * (([Inserted].[Price] /
(SELECT TOP 1 Price FROM FruitTrades WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate])) - 1.0 ))
),
-- Now Update the Action ID column
[FruitTrades].[TAID] =
(
CASE
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 1
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 2
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 3
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 4
ELSE NULL
END
)
FROM FruitTrades
INNER JOIN Inserted ON Inserted.FID = FruitTrades.FID AND Inserted.TradeDate = FruitTrades.TradeDate
END
-- STEP 3 (Create the PriceData table)
CREATE TABLE [dbo].[PriceData](
[FID] [nchar](3) NOT NULL,
[TradeDate] [smalldatetime] NOT NULL,
[Price] [real] NULL,
[Vol] [real] NULL,
CONSTRAINT [PK_PriceData] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
-- STEP 4 (simulate data import into PriceData table)
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/30/2012', 200, 1000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/29/2012', 190, 1200);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/28/2012', 195, 1250);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/27/2012', 205, 1950);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/26/2012', 200, 2000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/25/2012', 180, 1300);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/24/2012', 185, 1250);
-- STEP 5 (move price vol date from PriceDate table to Fruit Tables)
INSERT INTO FruitTrades (FID, TradeDate, Price, Vol) SELECT FID, TradeDate, Price, Vol FROM PriceData;
-- STEP 6 (check the FruitTrades table for correctness)
SELECT * FROM FruitTrades ORDER BY TradeDate
--- Results
After Step 6 you will find that the TAID and VolTrendPrevD in the FruitTrades table columns remain NULL.
Any help on how to resolve this problem is highly appreciated.
After laboring on this problem for 5 days, and some Googling, I finally found the solution myself.
The first problem was on account of my lack of understanding of triggers and how they fire. As per SQL documentation,
SQL Server triggers fire only once per statement, not once per
affected row
Due to this design principle, when in Step 5, I do a bulk Insert from the PriceData table into the FruitTrades table, the trigger is fired only once, instead of once for each row. Hence the Updated values are incorrect.
The VolTrendPrevD remains null because the Select statement for it in the Update trigger always matches the first row in the FruitTrades table (since the Inserted table has multiple rows) and for this row, the VolTrend is null.
The TAID remains null because VolTrendPrevD is null.
Now the fix:
Import the text file containing the price data into a MSaccess table. From there do a bulk insert in to the SQL Server table (using Linked tables). This approach uses ODBC to convert the bulk insert into multiple single inserts, thus bypassing the first problem.
Convert the VolTrend into a computed column. There is no need to update it therefore in the trigger.
Introduce an additional column PricePrevD in the FruitTrades table and update its value in the trigger, in the same manner as the VolTrendPrevD column.
Most importantly, ensure that the inserted rows from Access are inserted in ascending order by date (by creating an appropriate date index in Access). Else the desired results will be missing.
Hope this helps... :-)