Select all individuals who have certain properties on a given date - sql-server

I use dynamic properties to manage the characteristics of individuals. I have a table IndividusDynPropValues with 1 line for each individual and each property For example:
ID StartDate ValueString ValueFloat ValueDate Individus_ID IndividusDynProp
14 2018-09-10 Outside NULL NULL 3 Out Status
13 2018-08-15 Dead NULL NULL 1 Out Status
12 2018-08-02 Male NULL NULL 3 Sex
11 2018-07-28 #DBNULL# NULL NULL 1 Out Status
10 2018-07-25 Sold NULL NULL 1 Out Status
9 2018-06-07 Unk NULL NULL 3 Sex
8 2018-06-07 Adult NULL NULL 3 Status
7 2018-06-06 Femal NULL NULL 2 Sex
6 2018-06-06 Adult NULL NULL 2 Status
5 2018-06-03 Male NULL NULL 1 Sex
4 2018-06-03 Adult NULL NULL 1 Status
3 2018-05-23 Egg NULL NULL 3 Status
2 2018-05-23 Egg NULL NULL 2 Status
1 2018-05-21 Egg NULL NULL 1 Status
'#DBNULL#' means that the individual is on site again. I want to create a function that takes a date for the input parameter and returns a table with the ID of each individual who belong to the groups “Male living on site” at the date. An individual is part of the group if Sex = 'Male' AND (Out Status IS NULL OR Out Status = '#DBNULL#')
So in the example, if the date enter in the function is “2018-07-20” it returns:
Individus_ID
1
if the date is “2018-08-10” it returns:
Individus_ID
3
1
if the date is “2018-08-17” it returns:
Individus_ID
3
I’ve tried that:
CREATE FUNCTION fn_Groupe_Individus_Male_Vivant (#daDate datetime)
RETURNS #TGrMalesVivants TABLE (
TList_Individual [varchar] (50) NOT NULL
)
AS
BEGIN
DECLARE #TtempGrMalesVivants TABLE (TList_Individual [varchar] (50) NOT NULL)
DECLARE #TtempGrMales TABLE (TList_MIndividual int NOT NULL)
INSERT INTO #TtempGrMales
SELECT DISTINCT Individus_ID
FROM IndividusDynPropValues
DECLARE #selectedOutStatus VARCHAR(50),
#sBirdId int,
#tmpSex VARCHAR(3)
WHILE EXISTS (SELECT * FROM #TtempGrMales)
BEGIN
SELECT TOP 1 #sBirdId = TList_MIndividual FROM #TtempGrMales
SELECT #selectedOutStatus =IDPV.ValueString
FROM [dbo].[IndividusDynPropValues] AS IDPV
WHERE NOT EXISTS (SELECT *
FROM [dbo].[IndividusDynPropValues] AS IDPV2
WHERE IDPV.Individus_ID=IDPV2.Individus_ID
AND IDPV.IndividusDynProp_ID=IDPV2.IndividusDynProp_ID
AND IDPV2.StartDate>IDPV.StartDate AND CONVERT(smalldatetime,IDPV2.StartDate,120 )<=#daDate)
AND CONVERT(smalldatetime,IDPV.StartDate,120 )<=#daDate
AND IDPV.Individus_ID =#sBirdId
AND IDPV.IndividusDynProp_ID='Out Status'
SELECT #tmpSex= IDPV.ValueString
FROM [dbo].[IndividusDynPropValues] AS IDPV
INNER JOIN TSaisie AS TS ON TS.TSai_PK_ID=IDPV.Saisie_ID
INNER JOIN TProtocole AS TP ON TP.TPro_PK_ID=TS.TSai_FK_TPro_ID
WHERE NOT EXISTS (SELECT *
FROM [dbo].[IndividusDynPropValues] AS IDPV2
WHERE IDPV.Individus_ID=IDPV2.Individus_ID
AND IDPV.IndividusDynProp_ID=IDPV2.IndividusDynProp_ID
AND IDPV2.StartDate>IDPV.StartDate AND CONVERT(smalldatetime,IDPV2.StartDate,120 )<=#daDate)
AND CONVERT(smalldatetime,IDPV.StartDate,120 )<=#daDate
AND IDPV.Individus_ID =#sBirdId
AND IDPV.IndividusDynProp_ID='Sex'
AND TPro_Importance = (SELECT Max(TP2.Tpro_Importance)
FROM IndividusDynPropValues IDPV3
INNER JOIN TSaisie AS TS2 ON TS2.TSai_PK_ID=IDPV3.Saisie_ID
INNER JOIN TProtocole AS TP2 ON TP2.TPro_PK_ID=TS2.TSai_FK_TPro_ID
WHERE IDPV3.Individus_ID=#sBirdId
AND IDPV3.IndividusDynProp_ID=4
AND IDPV3.StartDate>=IDPV.StartDate
AND CONVERT(smalldatetime,IDPV3.StartDate,120 )<=#daDate)
IF #tmpSex='Male' AND (#selectedOutStatus IS NULL OR #selectedOutStatus='#DBNULL#')
BEGIN
INSERT INTO #TtempGrMalesVivants (TList_Individual)
VALUES (#sBirdId)
END
DELETE TOP (1) FROM #TtempGrMales
END
INSERT #TGrMalesVivants
SELECT *
FROM #TtempGrMalesVivants
RETURN
END
it works but it takes 1:55 with all the table (1859732 lines) so it’s too long

I think I understand what you are trying to do. Notice how I posted sample data? This is a good example for what you should do in the future.
Some conditional aggregation should work here. You need this aggregation because your data is denormalized into an EAV and you need to reassemble this into a normalized table. This works for the sample data and your desired output. You can uncomment the set #MyDate line to see the second value working.
if OBJECT_ID('tempdb..#IndividusDynPropValues') is not null
drop table #IndividusDynPropValues
create table #IndividusDynPropValues
(
ID int
, StartDate date
, ValueString varchar(50)
, ValueFloat float
, ValueDate date
, Individus_ID int
, IndividusDynProp varchar(50)
)
insert #IndividusDynPropValues values
(14, '2018-09-10', 'Outside', NULL, NULL, 3, 'Out Status')
, (13, '2018-08-15', 'Dead', NULL, NULL, 1, 'Out Status')
, (12, '2018-08-02', 'Male', NULL, NULL, 3, 'Sex')
, (11, '2018-07-28', '#DBNULL#', NULL, NULL, 1, 'Out Status')
, (10, '2018-07-25', 'Sold', NULL, NULL, 1, 'Out Status')
, (9 , '2018-06-07', 'Unk' , NULL, NULL, 3, 'Sex')
, (8 , '2018-06-07', 'Adult' , NULL, NULL, 3, 'Status')
, (7 , '2018-06-06', 'Femal' , NULL, NULL, 2, 'Sex')
, (6 , '2018-06-06', 'Adult' , NULL, NULL, 2, 'Status')
, (5 , '2018-06-03', 'Male' , NULL, NULL, 1, 'Sex')
, (4 , '2018-06-03', 'Adult' , NULL, NULL, 1, 'Status')
, (3 , '2018-05-23', 'Egg' , NULL, NULL, 3, 'Status')
, (2 , '2018-05-23', 'Egg' , NULL, NULL, 2, 'Status')
, (1 , '2018-05-21', 'Egg' , NULL, NULL, 1, 'Status')
declare #MyDate date = '20180720' --returns Individus_ID 1
--set #MyDate = '20180810' --returns Individus_ID 1, 3
;
with MySortedData as
(
select i.*
, RowNum = ROW_NUMBER()over(partition by i.Individus_ID, i.IndividusDynProp order by i.StartDate desc)
from #IndividusDynPropValues i
where i.StartDate <= #MyDate
)
select s.Individus_ID
, OutStatus = max(case when IndividusDynProp = 'Out Status' then ValueString end)
, Status = max(case when IndividusDynProp = 'Status' then ValueString end)
, Sex = max(case when IndividusDynProp = 'Sex' then ValueString end)
from MySortedData s
where s.RowNum = 1
group by s.Individus_ID
having isnull(max(case when IndividusDynProp = 'Out Status' then ValueString end), '#DBNULL#') = '#DBNULL#'
and max(case when IndividusDynProp = 'Sex' then ValueString end) = 'Male'

Related

HAVING clause to filter out null rows ONY if aggregate grouping contains not null values

I'm trying to create an aggregate report from a table with schema
PersonName varchar, IDNum int, CreateDate date
PersonName and CreateDate are not null, however, IDNum is nullable, and there is no unique constraints on this table so that the same PersonName can have multiple`IDNums and even multiple null IDNums.
What I'm trying to do is create a report that groups by the CreateDate that ignores null IDNums if there exist ANY not null values for that date
So, if on February 1, a person exists with both IDNum 20,21,23,NULL,NULL the report aggregates 20, 21, 23 on the same date and ignores the nulls, if another person on February 1 exists with just NULL IDNum, they show up just once as NULL. If that person also exists on February 2 as IDNums 25, 30, NULL, they show up once in February 1 as NULL and again in February 2 as 25, 30 only. The null can be replaced with some other default value. It's not important.
So far I've got
SELECT PersonName, COALESCE(IDNum, 0) IDNum, CreateDate FROM tbl
GROUP BY PersonName, COALESCE(IDNum, 0), CreateDate
HAVING COALESCE(IDNum, 0) >= CASE MAX(COALESCE(IDNum, 0)) WHEN 0 THEN 0 ELSE 1 END
The idea here being if the IDNum evaluates to 0, don't evaluate it in the grouping if another IDNum evaluates to greater than 0 but this doesn't appear to be working. The results are identical as if the having clause just wasn't there and I can visually see several results of the same PersonName that contain both IDNums that are 0 and nonzero on the same date.
Check this query. Just remove duplicating null values. Then count IDNum. Depending on that show your desired result
declare #t table (
PersonName varchar(10)
, IDNum int
, CreateDate date
)
insert into #t
values
('A', 20, '20180216')
,('A', 21, '20180216')
,('A', null, '20180216')
,('A', null, '20180216')
,('B', null, '20180216')
,('C', null, '20180216')
,('C', null, '20180216')
select
PersonName, IDNum, CreateDate
from (
select
*, cnt = count(*) over (partition by PersonName, CreateDate)
from
#t
group by PersonName, IDNum, CreateDate
) t
where
cnt = 1
or (cnt > 1 and IDNum is not null)
Output
PersonName, IDNum, CreateDate
-----------------------------
A 20 2018-02-16
A 21 2018-02-16
B NULL 2018-02-16
C NULL 2018-02-16

Summarizing SQL table

I have a SQL table that looks something like this:
Table definition:
CREATE TABLE TestSessions (Id INTEGER IDENTITY(1,1) PRIMARY KEY not null, TestUser varchar(50) not null, Department varchar(50) not null, Project varchar(50) not null, TestDateTime varchar(50) not null, Score varchar(50) not null, Pass varchar(50) not null);
I would like to group the table by TestUser and count each Pass (Passes and Fails separately) value for each TestUser so that it should look like:
SQL command: "SELECT TestUser, SUM(CASE WHEN [Score] = 'Pass' THEN 1 ELSE 0 END)[Pass], SUM( CASE WHEN[Score] = 'Fail' THEN 1 ELSE 0 END)[Fail] FROM TestSessions GROUP BY TestUser"
Returns:
How can I fix this?
Then I would like to calculate rate between Passes and Fails.
Is that possible in one statement?
You need a simple aggregation and a CASE expression:
SELECT Color,
SUM(CASE WHEN [BOOL] = 'TRUE' THEN 1 ELSE 0 END) [True],
SUM(CASE WHEN [BOOL] = 'FALSE' THEN 1 ELSE 0 END) [False]
FROM dbo.YourTable
GROUP BY Color;
This assumes that the datatype of BOOL is a varchar, if it's a bit, then you need to do:
SELECT Color,
SUM(CASE WHEN [BOOL] = 1 THEN 1 ELSE 0 END) [True],
SUM(CASE WHEN [BOOL] = 0 THEN 1 ELSE 0 END) [False]
FROM dbo.YourTable
GROUP BY Color;
First of all, your CASE should check on the column [Pass] instead of [Score], since [Score] never contains "Pass" or "Fail. Secondly, you could try and write a cte around it, like the following:
WITH cte AS (
SELECT TestUser
,Department
,CASE WHEN [Pass] = 'Pass' THEN 1 ELSE 0 END [Passed]
,CASE WHEN[Pass] = 'Fail' THEN 1 ELSE 0 END [Failed]
FROM TestSessions
)
SELECT TestUser
,Department
,sum([Passed]) [Pass]
,sum([Failed]) [Fail]
FROM cte
GROUP BY TestUser, Department

sql server 2014 group by date extract several fields

I have a table that looks like this:
CREATE TABLE dbo.Mails (
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
Email nvarchar(70) NOT NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced datetime NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
)
I need to show info on a chart, and I need to group by Date.
therefore if I want to group details by ReadOn field for a certain campaign I build the following query
Select
CAST(readOn as date) [date],
COUNT(*) [read]
FROM Mails m
WHERE m.Reference=#Reference
GROUP BY CAST(readOn as date)
ORDER BY CAST(readOn as date) ASC
and I get something like this:
sDate read
NULL 360
2016-05-05 67
2016-05-06 123
2016-05-07 84
2016-05-08 62
2016-05-09 89
2016-05-10 17
2016-05-11 12
2016-05-12 8
2016-05-13 4
2016-05-14 4
But I would like to extract, in the same query, not only ReadOn field, but also other fields like ETS, ATS, Unsubscribed/Read & Unread and Bounced
and get something like this
sDate read ETS ATS Bounced Unsub./Read Unsub/Unread
NULL 360
2016-05-05 67 830 570 27 7 3
2016-05-06 123 0 260 4 9 5
2016-05-07 84 0 0 0 2 2
2016-05-08 62 0 0 0 2 4
2016-05-09 89 0 0 0 7 1
2016-05-10 17 0 0 0 5 6
2016-05-11 12 0 0 0 8 2
2016-05-12 8 0 0 0 1 3
2016-05-13 4 0 0 0 0 2
2016-05-14 4 0 0 0 0 2
Is there an easier way than building 6 different queries?
can at least indicate the path to follow?
Thanks
Joe
You can do it with some pre-processing and a PIVOT. In this example, I've put the query into a stored procedure, so that it is contained and easy to test. I'm doing the pre-processing in a CTE, to keep the main query tidy.
First, create the table and populate it.
CREATE TABLE dbo.Mails
(
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced bit DEFAULT 0 NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
);
GO
INSERT INTO dbo.Mails (Reference, ETS, ATS, ReadOn, Unsubscribed, Bounced)
VALUES
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-05', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-08', NULL, 0),
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-07', NULL, 0),
(N'ABC', '2015-05-07', '2015-05-08', '2015-05-09', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-09', '2015-05-09', 0),
(N'ABC', '2015-05-06', '2015-05-07', NULL, '2015-05-08', 0);
Then create a stored procedure with a parameter #Reference. I'm using a CTE to create a two column row set, with Date and Type as the columns. Then, in the main SELECT statement it's being pivoted to give the result you want.
The row set produced by the CTE looks like this.
Note: I haven't included the Bounced column, because I'm not clear what the requirement is for that; it's not a date column. However you should be able to extend this example quite easily.
CREATE PROCEDURE dbo.up_ReportMails
(
#Reference nvarchar(20)
)
AS
WITH cte AS
(
SELECT CAST(ReadOn AS date) AS 'Date', 'R' AS 'Type'
FROM dbo.Mails
WHERE Reference = #Reference AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(ETS AS date), 'E'
FROM dbo.Mails
WHERE Reference = #Reference AND ETS IS NOT NULL
UNION ALL
SELECT CAST(ATS AS date), 'A'
FROM dbo.Mails
WHERE Reference = #Reference AND ATS IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'U'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'V'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NULL
)
SELECT [Date], [R] AS 'Read', [E] AS 'ETS', [A] AS 'ATS', [U] AS 'Unsub/Read', [V] As 'Unsub/Unread'
FROM
(SELECT [Date], [Type]
FROM cte) AS C
PIVOT
(
COUNT([Type])
FOR [Type] IN ([R], [E], [A], [U], [V])
) AS PivotTable
ORDER BY [Date];
Then we can test it.
EXEC dbo.up_ReportMails #Reference=N'ABC';
I've tested this code and it works. Assuming you have a calendar table (if you don't have one, you can Google it and make one in about 10 minutes - they're very simple and will save you loads of time):
DECLARE #StartDate date = '01/01/2016'
DECLARE #EndDate date = '05/06/2016'
SELECT C.BaseDate,
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ETS AS DATE) THEN 1 END), 0) AS [ETS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ATS AS DATE) THEN 1 END), 0) AS [ATS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ReadOn AS DATE) THEN 1 END), 0) AS [Read On],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Unsubscribed AS DATE) THEN 1 END), 0) AS [Unsubscribed],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Bounced AS DATE) THEN 1 END), 0) AS [Bounced]
FROM Calendar C
LEFT OUTER JOIN Mails M
ON C.BaseDate = CAST(M.ETS AS DATE)
OR C.BaseDate = CAST(M.ATS AS DATE)
OR C.BaseDate = CAST(M.ReadOn AS DATE)
OR C.BaseDate = CAST(M.Unsubscribed AS DATE)
OR C.BaseDate = CAST(M.Bounced AS DATE)
WHERE C.BaseDate BETWEEN #StartDate AND #EndDate
GROUP BY C.BaseDate
Basically what you're doing is selecting every date from the calendar table within your date range, and then joining it to your mails table if ANY of the datetimes match that date. The purpose of the left join is so that dates on which nothing occurs are still returned in your result set. They will all be zeros, but it's better for consistency and in case someone wants to calculate averages from your report.
Once you have all the dates - and all of the records that have a matching datetime, you just need to count how many, for each date, have a matching ETS, how many have a matching ATS, so on and so forth. Last, you group by the calendar date and you're all done.

How to calculate the total number of pair under particular parent according to pattern 1:2,1:1 day to day maximum up to 5 pair caping daily

I am working on a project in which members are added in a tree pattern, and get the payment accordingly.
below is my table structure ,data and stored procedure
CREATE TABLE Associate_Income
(
ID varchar(30) NOT NULL,
ParentID varchar(30) NULL,
IsLeft tinyint NULL,
IsRight tinyint NULL,
joingdate datetime NOT NULL
)
INSERT Associate_Income
(ID, ParentID, IsLeft, IsRight, joingdate)
SELECT 'Ramesh123', NULL, NULL, NULL '2014-01-03 16:31:15.000' UNION ALL
SELECT 'Sonu', 'Ramesh123', 1, NULL, '2014-01-03 16:45:21.000' UNION ALL
SELECT 'Pawan kumar', 'Ramesh123', NULL, 1, '2014-01-04 16:50:23.000' UNION ALL
SELECT 'Ravi123', 'Sonu', 1, NULL, '2014-01-04 17:03:22.000' UNION ALL
SELECT 'Vineet123', 'Sonu', NULL, 1, '2014-01-04 17:26:01.000' UNION ALL
SELECT 'dev123', 'Ravi123', 1, NULL, '2014-01-05 19:35:16.000' UNION ALL
SELECT 'Mukesh123', 'Ravi123', NULL, 1, '2014-01-05 19:40:41.000' UNION ALL
SELECT 'poonam123', 'Vineet123', 1, NULL, '2014-01-05 19:49:49.000' UNION ALL
SELECT 'monu', 'Pawan kumar', 1, NULL, '2014-01-05 17:32:58.000' UNION ALL
SELECT 'Arti123', 'Pawan kumar', NULL, 1, '2014-01-05 19:54:35.000' UNION ALL
My database table Associate_Income structure is as follow:
ID ParentID IsLeft IsRight joingdate
Ramesh123 NULL NULL NULL 2014-01-03 16:31:15.000
Sonu Ramesh123 1 NULL 2014-01-03 16:45:21.000
Pawan kumar Ramesh123 NULL 1 2014-01-04 16:50:23.000
Ravi123 Sonu 1 NULL 2014-01-04 17:03:22.000
Vineet123 Sonu NULL 1 2014-01-04 17:26:01.000
dev123 Ravi123 1 NULL 2014-01-05 19:35:16.000
Mukesh123 Ravi123 NULL 1 2014-01-05 19:40:41.000
poonam123 Vineet123 1 NULL 2014-01-05 19:49:49.000
monu Pawan kumar 1 NULL 2014-01-05 17:32:58.000
Arti123 Pawan kumar NULL 1 2014-01-05 19:54:35.000
by using below stored procedure I can count the total number of pairs under particular node in 2:1,1:1 ratio means first pair is completed when two node added to the left side of given parent node and one node added right side of given parent node after that all pairs are completed when one node added left side and one node added right side of parent node (1:1 ratio).
example if i execute my stored procedure as follows it would return following.
EXEC count_pairs 'Ramesh123'
3
so there is 3 pairs as shown in my figure.
when we execute my stored procedure for ParentID 'sonu' it would return following.
EXEC count_pairs 'sonu'
2
it return 2
means node 'sonu' have total 2 pairs as shown my figure but
my problem is
how to count total number of pairs which are completed by node 'sonu' in day ('2014-01-03 16:31:15.000') and total number of pairs in day(2014-01-04 16:31:15.000) and total number of pairs in day(2014-01-05 16:31:15.000)
.
.
.
. up to last date as given
CREATE proc [dbo].[count_pairs]
(
#ParentID nvarchar(50)
)
as
begin
Declare #ParentSUM SMALLINT = 0
Declare #SubLeftID nvarchar(50)
Declare #SubRightID nvarchar(50)
SELECT #SubLeftID = CASE WHEN [IsLeft] = 1 THEN [ID] ELSE #SubLeftID END
,#SubRightID = CASE WHEN [IsRight] = 1 THEN [ID] ELSE #SubRightID END
FROM Associate_Income
WHERE ParentID = #ParentID
IF #SubLeftID IS NOT NULL AND #SubRightID IS NOT NULL AND EXISTS(SELECT 1 FROM Associate_Income
WHERE [IsLeft] = 1 AND ParentID = #SubLeftID)
BEGIN
SET #ParentSUM = 1
;WITH Associate_Income_CTE AS
(
SELECT [ID], [ParentID], [IsLeft], [IsRight], 0 AS [Level]
FROM Associate_Income
WHERE [ParentID] = #ParentID
UNION ALL
SELECT RecursiveMember.[ID], RecursiveMember.[ParentID], RecursiveMember.[IsLeft], RecursiveMember.[IsRight], Level + 1
FROM Associate_Income RecursiveMember
INNER JOIN Associate_Income_CTE AnchorMember
ON RecursiveMember.[ParentID] = AnchorMember.[ID]
)
SELECT #ParentSUM = #ParentSUM + COUNT([ParentID])
FROM
(
SELECT [ParentID]
,'IsLeft' AS [Direction]
,1 AS [Value]
FROM Associate_Income
WHERE [IsLeft] = 1
AND [ID] <> #ParentID --AND [ID] NOT IN (#SubLeftID, #ParentID)
AND [ParentID] NOT IN (#ParentID, #SubLeftID)
UNION ALL
SELECT [ParentID]
,'IsRight' AS [Direction]
,1 AS [Value]
FROM Associate_Income
WHERE [IsRight] = 1
AND [ParentID] <> #ParentID
) AS Associate_Income
PIVOT
(
MAX([Value]) FOR [Direction] IN ([IsLeft], [IsRight])
) PVT
WHERE [IsLeft] IS NOT NULL AND [IsRight] IS NOT NULL
END
SELECT #ParentSUM
Dear friends following is the result which i want when executing my stored procedure as follows for node 'Ramesh123'
exec count_pairs 'Ramesh123'
Result
Joingdate Completed Pair
2014-01-03 13:54:35.000 0
2014-01-04 14:56:35.000 0
2014-01-05 15:58:35.000 0
2014-01-06 16:54:35.000 1
2014-01-07 14:54:35.000 0
2014-01-07 15:34:35.000 1
2014-01-07 15:54:35.000 0
2014-01-09 11:54:35.000 0
2014-01-09 14:64:35.000 0
2014-01-23 17:54:35.000 1
and for 'sonu'
exec count_pairs 'sonu'
Result
Joingdate Completed Pair
2014-01-03 13:54:35.000 0
2014-01-04 14:56:35.000 0
2014-01-05 15:58:35.000 0
2014-01-06 16:54:35.000 0
2014-01-07 14:54:35.000 0
2014-01-07 15:34:35.000 0
2014-01-07 15:54:35.000 1
2014-01-09 11:54:35.000 0
2014-01-09 14:64:35.000 1
2014-01-23 17:54:35.000 0
please any one can give me some your valuable advice ,idea or solution
thanks in advance

CTE - recursively update quantity until total consumed

I've been researching CTEs trying to determine if it's possible to recursively update inventory quantity records with an order quantity until the order quantity is consumed.
Here are the tables and records:
CREATE TABLE [dbo].[myOrder](
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myOrder values (12345, 1, 50)
CREATE TABLE [dbo].[myInventory](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Account] [float] NOT NULL,
[InvDate] [numeric](18, 0) NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL,
[QuantitySold] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myInventory values (12345, 111287, 1, 45, 40)
insert into dbo.myInventory values (12345, 111290, 1, 40, 0)
insert into dbo.myInventory values (12345, 111290, 1, 12, 0)
insert into dbo.myInventory values (12345, 111291, 1, 25, 0)
The record in the myOrder table indicates that an order is to be created for account 12345 for item #1, quantity 50:
Account Item Quantity
------- ---- --------
12345 1 50
The inventory table shows that we have plenty of item #1 on hand for account 12345:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 40
2 12345 111290 1 40 0
3 12345 111290 1 12 0
4 12345 111291 1 25 0
The goal is to start plugging in the order quantity of 50 into the inventory records until all 50 are consumed. Inventory records are ordered by the value in the InvDate column. Record 1 has 5 remaining quantity (45 - 40 = 5), which would leave us with 45 more to consume for the order. Record 2 can consume 40. Record 3 can consume the last 5. When the query completes the inventory records would look like this:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 45
2 12345 111290 1 40 40
3 12345 111290 1 12 5
4 12345 111291 1 25 0
Note: The inventory table stores QuantitySold, not QuantityRemaining, so you have to do the math (Quantity - QuantitySold) to determine how much quantity remains per inventory record.
I've gotten almost nowhere with the CTE. I've found plenty of examples for doing selects where you have 2 parts to your CTE - an initialization part and the recursive part UNIONed together. I could write this with a cursor, but I think it's possible to do with a CTE and I'd like to learn how.
If anyone can confirm this is possible with a CTE or explain how to set up the CTE, I'd appreciate it. Thanks!
--#inserted table mimics inserted virtual table from AFTER INSERT triggers on [dbo].[myOrder] table
DECLARE #inserted TABLE
(
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
);
INSERT #inserted
VALUES (12345, 1, 50);
WITH CteRowNumber
AS
(
SELECT inv.ID
,inv.Account
,inv.Item
,inv.Quantity
,inv.QuantitySold
,i.Quantity QuantityOrdered
,ROW_NUMBER() OVER(PARTITION BY inv.Account,inv.Item ORDER BY inv.ID ASC) RowNumber
FROM myInventory inv
INNER JOIN #inserted i ON inv.Account = i.Account
AND inv.Item = i.Item
WHERE inv.Quantity > inv.QuantitySold
), CteRecursive
AS
(
SELECT a.ID
,a.Account
,a.Item
,a.RowNumber
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END QuantitySoldNew
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END RunningTotal
FROM CteRowNumber a
WHERE a.RowNumber = 1
UNION ALL
SELECT crt.ID
,crt.Account
,crt.Item
,crt.RowNumber
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN crt.Quantity - crt.QuantitySold
ELSE crt.QuantityOrdered - prev.RunningTotal
END QuantitySoldNew
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold)
ELSE crt.QuantityOrdered
END RunningTotal
FROM CteRecursive prev
INNER JOIN CteRowNumber crt ON prev.Account = crt.Account
AND prev.Item = crt.Item
AND prev.RowNumber + 1 = crt.RowNumber
WHERE prev.RunningTotal < crt.QuantityOrdered
)
SELECT cte.ID
,cte.Account
,cte.Item
,cte.QuantitySoldNew
FROM CteRecursive cte;
--or CteRecursive can be used to update QuantitySold column from [dbo].[myInventory] table
--UPDATE myInventory
--SET QuantitySold = inv.QuantitySold + cte.QuantitySoldNew
--FROM myInventory inv
--INNER JOIN CteRecursive cte ON inv.ID = cte.ID;

Resources