FOREACH Recursive SQL Statement - c#

I have what would seem to be a simple problem but the solutions I have tried to date have left me wanting in the execution areas. Speeds seem fine on small (<10000) data sets but quickly take more and more time as the counts go up.
In SQL Server 2008 R2 I have a table with four columns in it: Id, ParentId, ControlNum, ParentControlNum.
The Id and ParentId information is filled in. An Id always has a value, the ParentId is null if the row has no parent, otherwise its the the value of an Id within the table that represents the parent row.
The issue is that the Id and ParentIds are all over the place. All of the Ids are added to the table then they are processed to add the children. This is a set part of the problem and is not something that can be changed.
What I need to be able to do is to generate the ControlNum values in order obeying the Parent Child relationship. My current logic uses a bit of C# and SQL SELECT/UPDATE commands to accomplish this, but as mentioned performance is a great concern.
In Pseudo-Code
Select all Id's where the parent Id is null (All root entries)
Foreach (Id)
GenerateControlNum(Id, CurrentCounterValue, CurrentCounterValue)
GenerateControlNum(Id, CurrentCounterValue, ParentCounterValue)
Set Id's ControlNum to CurrentCounterValue
Set Id's ParentControlNum to CurrentCounterValue
Increment CurrentCounterValue
Select All Id's where ParentId == Id (All my direct children)
Foreach (ChildId)
GenerateControlNum(ChildId, CurrentCounterValue, Id's ControlNum);
Baseline is trying to make this execute faster, idealy completely in SQL would be preffered. I am trying along the lines of a CTE populated with the RootIds and then going through them with a MERGE statement but I can just not seem to get the counter value to work properly for setting the ControlNum values.
Is this even possible in SQL or is this too much of a procedural type of processing?
Example Table Data from how it currently runs: BEFORE
ID ParentId ControlNum ParentControlNum
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0 NULL
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0 NULL
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 0 NULL
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 0 NULL
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0 NULL
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0 NULL
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 0 NULL
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 NULL
AFTER
ID ParentId ControlNum ParentControlNum
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 22 21
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 24 21
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 58 57
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 69 68
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 86 85
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 88 85
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 95 94
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL 0 0
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL 1 1
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL 2 2
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL 3 3
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL 4 4
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL 5 5
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL 6 6
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL 7 7
The data set I have is 104 entries right now so this is just the first 15. The objects with out parents are at the bottom, those are examples of root entries and have their control number and parent control number set to the same value. At the top of the table we see a few objects that have the same parents and so have matching parent control numbers and fairly close control numbers themselves (there must be row between ControlNum 22 and 24 also from parent 21 for example. same for the 86 to 88 jump, they are just not in the table next to each other).
Hope this makes it more clear.
EDIT: More clarity based on the answer given by Mikael
Below are ControlNum values displayed in the hierarchy based on their Id and ParentId information. Normally these would be listed 1, 2, 3 ... 8 but its easier not to clutter up the display with (child of) messages all over.
1
4
7
8
5
2
6
3
What I need is
1
2
3
4
5
6
7
8
This is why recursion has been the way that I have been going is I need to assign a ControlNum to the root object, and then the next object needs to be its first child followed by that objects children and so on before going on to the next root object.
I guess what I am saying is this is a breadth first and what I am in need of is a depth first.

Not sure I get all of your requirement but here is a start. Tell me if it does what you want or if the numbers are not generated correctly.
;with C as
(
select ID,
ParentID,
ControlNum,
ParentControlNum,
row_number() over(order by ParentID, ID) - 1 as rn
from YourTable
)
update C1
set ControlNum = C1.rn,
ParentControlNum = case when C1.ParentID is null
then C1.rn
else C2.rn
end
from C as C1
left outer join C as C2
on C1.ParentID = C2.ID
Run it on SE-Data with slightly modified input: https://data.stackexchange.com/stackoverflow/q/115625/
Version 2
First a recursive CTE R,that builds a string to be used as order by when generating values for ControlNum. After that it is pretty much the same as above.
;with R as
(
select ID,
ParentID,
cast(ID as varchar(max)) as Sort
from YourTable
where ParentID is null
union all
select T.ID,
T.ParentID,
R.Sort+cast(T.ID as varchar(max))
from YourTable as T
inner join R
on R.ID = T.ParentID
),
C as
(
select ID,
ParentID,
row_number() over(order by Sort) - 1 as rn
from R
)
update T
set ControlNum = C1.rn,
ParentControlNum = case when C1.ParentID is null
then C1.rn
else C2.rn
end
from YourTable as T
inner join C as C1
on T.ID = C1.ID
left outer join C as C2
on T.ParentID = C2.ID
Test here: https://data.stackexchange.com/stackoverflow/q/115626/
Note: I guess this is a one time thing you will do with some data because you will have a hard time adding new nodes and at the same time uphold the numbering like this. If you for instance add a new child node to the first node you will have to assign all ControlNum += 1 for all nodes "below" and reassign all ParentControlNum.

Related

What is the best way to update a table column based on a result of a sql select query C#?

The original query :
with Tr As (
SELECT
DocDtls.Warehouse,
Transactions.Code,
DocDtls.zDate,
Transactions.ID,
Transactions.QtyIn,
Transactions.QtyOut,
Transactions.BalanceAfter
FROM
DocDtls
INNER JOIN Transactions ON DocDtls.[PrimDocNum] = Transactions.[DocNum]
),
formatted_tr as (
select
ID,
Code,
QtyIn,
QtyOut,
BalanceAfter,
LAG(BalanceAfter, 1, 0) Over (
partition by Warehouse,
Code
order by
Code,zDate,ID
) Prev_BlncAfter
from
Tr
)
select ID,Code,QtyIn,QtyOut,BalanceAfter
,SUM(Prev_BlncAfter + QtyIn)-QtyOut As NewBlncAfter
from formatted_tr
group by ID,Code,QtyIn,QtyOut,BalanceAfter;
;
Explaining the idea :
Let's say that the query returns all transactions of Item X and there are 10 rows as result , I need to loop through all 10 rows and SET BalanceAfter( for the first transaction QtyIn-QtyOut , Any other transaction (PreviousBalanceAfter+QtyIn)-QtyOut) And so on .
What I've tried :
I tried to put the query result in a Datatable then filter it one more time using DataView to get the NewBlncAfter of the DataGridView current row ID only so the Dataview only have one row and save it in a variable - Working well so far - when I try to loop through all rows in my DataGridview and update BalanceAfter I got :
Must Declare Scalar Variable #Newblnc
You can find the whole code in here :
My Code
So Is there a direct way to update all transactions BalanceAfter to equal
EDIT #1 : I used #Charliface query and the result was :
I used the old query to compare the results , The BalanceAfter should equal NewBlncAfter in every row .
Edit #2 : Using SUM instead of LAG causing wrong calculation and if I used the query more than once the result in BalanceAfter is multiplied
ID Code QtyIn QtyOut BalanceAfter
9 100001 20000 0 20000
14 100001 0 6000 40000
21 100001 3500 0 60000
24 100001 0 3000 80000
The main idea and the desired result for example :
ID Code QtyIn QtyOut BalanceAfter
9 100001 20000 0 20000
14 100001 0 6000 14000
21 100001 3500 0 17500
24 100001 0 3000 14500
The formula is :
for the first transaction QtyIn-QtyOut , Any other transaction (PreviousBalanceAfter+QtyIn)-QtyOut And so on .
Firstly, I see no reason at all to pull all this data into C# only to then update row-by-row (which is highly inefficient). You can do this in a single batch update.
It's not quite clear what result you want, but it seems you want to just assign a running SUM calculation, rather than LAG.
Furthermore:
The second CTE is unnecessary and can be collapsed into the first.
Partitioning and ordering by the same column in an OVER makes no sense.
The final GROUP BY also makes no sense and appears unnecessary, as you are grouping by a primary key.
WITH Tr AS (
SELECT
d.Warehouse,
t.Code,
d.zDate,
t.ID,
t.QtyIn,
t.QtyOut,
t.BalanceAfter,
SUM(t.QtyIn - t.QtyOut) OVER (
PARTITION BY d.Warehouse, t.Code
ORDER BY d.zDate, t.ID
ROWS UNBOUNDED PRECEDING
) BlncAfter
FROM
DocDtls d
INNER JOIN Transactions t ON d.PrimDocNum = t.DocNum
WHERE t.Code = #VariableCode
)
UPDATE Tr
SET BalanceAfter = BlncAfter;
One final point: why bother storing this information in a new column at all? Why not just calculate it when you need to, using SUM OVER?

How to write a recursive query with 2 tables in SQL Server

I have a table with the following structure.
Table name: Table0
The structure is as below
Select process from Table0 where Name like '%Aswini%'
Process
-------
112
778
756
All these process must go into the below table
Table name: Table1
The structure is as below
Select Exec, stepid, condition
from Table1
where Exec = 112
Exec stepid condition
-----------------------
112 2233 0
112 2354 0
445 3455 0
The second table 'Table 2' structure follows:
Select stepid, processid
from Table2
where stepid = 2233
Stepid processid
-----------------
2233 445
2354 566
3455 556
The Table1 stepid is input to Table2 stepid and Table2 Processid is input to Table1 Exec. I have to recursively get processID until the condition is 0 else the table returns no rows and the final processid is the parent ID.
I have not worked on CTE. So I have used a simple join to get the following result.
select b.processid
from Table1 a
inner join Table2 b on a.stepid = b.stepid
where a.condition = 0
and a.exec = 112(parent from table0)
The above query will give me parent of Exec 112 if it satisfies the condition.
I have to again input the parent to the query and execute it.
I can achieve this with the help of C# by putting it in a loop. But I want it in SQL Server alone. Is this achievable?
Edited
When I execute the CTE I get the below result
Process Parent
112 445
112 566
112 445
112 566
If the initial process has 2 exec then the final process parent structure is duplicated twice( number of exec). Why is this happening. It has to display the result only once.
A solution without a cursor (which I personally prefer):
WITH [CTE] AS
(
SELECT
T1.[Exec] AS [process],
1 AS [n],
T1.[Exec],
T1.[Exec] AS [parent]
FROM
[Table1] AS T1
UNION ALL
SELECT
C.[process],
C.[n] + 1,
T1.[Exec],
T2.[processid]
FROM
[CTE] AS C
INNER JOIN [Table1] AS T1 ON T1.[Exec] = C.[parent]
INNER JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
)
SELECT C.[process], C.[parent]
FROM [CTE] AS C
WHERE C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process])
Explanation:
The anchor part of the common table expression (the SELECT query before the UNION ALL) defines the starting point of the operation. In this case, it simply selects all data from Table1 and it has four fields:
process will contain the value of the process (Exec value) of which the parent should be determined.
n will contain a sequence number, starting with 1.
Exec will contain a "shifting" value for joining records in the next "recursive" part of the common table expression.
parent will contain the corresponding processid field from Table2, which represents the direct parent of the Exec value.
This anchor expression will produce the following data:
process n Exec parent
112 1 112 112
445 1 445 445
The recursive part of the common table expression (the SELECT query after the UNION ALL) keeps adding records to the CTE from Table1 (where its Exec value equals the parent value of the previous CTE record) and Table2 (related with Table1 on the stepid fields). Those newly added records in the CTE will have the following field values:
process will be copied from the previous CTE record.
n will be increased by 1.
Exec will get the Exec value of the joined Table1's Exec value (equal to the previous CTE record's parent value).
parent will - again - get the corresponding processid value from Table2 where its stepid value equals Table1's stepid value.
The entire CTE will yield the following results:
process n Exec parent
112 1 112 112
112 2 112 445
112 3 445 556
445 1 445 445
445 2 445 556
The main query (below the CTE) will select only the process and parent fields for each "last" record in the CTE (where the value of n is the largest value for that specific process value, which is determined using a subquery).
This produces the following end result:
process parent
445 556
112 556
Hope this helps a little.
Edit regarding the update in the question regarding 3rd table Table0:
Assuming that your query SELECT [process] FROM [Table0] WHERE [Name] LIKE '%Aswini%' will contain valid processes for the query above to return, only the WHERE-clause of the main query above needs to be changed.
Previous WHERE-clause:
WHERE C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process])
Updated WHERE-clause:
WHERE
C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process]) AND
C.[process] IN (SELECT [process] FROM [Table0] WHERE [Name] LIKE '%Aswini%')
Edit regarding possible duplicates when processes have more than one parent
In case a process has more than one parent (??), the above query produces duplicates. To eliminate the duplicates and to provide a more robust way for determining the topmost parent of a process, the following modifications were made:
The anchor part of the CTE puts the actual parent of a process in the parent field by joining Table1 to Table2. This join should be a left join, so that processes without parents (if possible) will be included in the results too; their parent value will be equal to their own process id.
The recursive part of the CTE should only add parents for processes that have an actual parent (where field process is not equal to parent). This is to avoid infinite loops in recursivity (if possible).
The main query should filter out all records where the value of the parent field is also used in another result record as the value of the exec field for the same base process (the value in the process field). Because in that case, the parent field is not the final parent value, and that other result record might be a more fitting candidate for containing the actual parent.
In other words: if process A has parent B, and process B has parent C, there are three related results in the CTE: (A, A, B), (A, B, C), and (B, B, C). Result (A, A, B) is invalid, because a more fitting candidate (A, B, C) is available in the results too. The final results should include (A, C) and (B, C), but not (A, B).
This logic is implemented using a subquery in an EXISTS operator in the WHERE clause, but it could also be realized using a LEFT JOIN on the CTE itself as well, of course.
Because of the upgraded logic described in point 3, the column n of the CTE is not used anymore and has been removed.
To avoid duplicates in case of a "diamond pattern" in the data (process A has parents B and C, and both processes B and C have parent D), a DISTINCT is used in the main query's SELECT clause to avoid duplicates (A, D).
The final query would look like this:
WITH [CTE] AS
(
SELECT
T1.[exec] AS [process],
T1.[exec],
COALESCE(T2.[processid], T1.[exec]) AS [parent]
FROM
[Table1] AS T1
LEFT JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
UNION ALL
SELECT
C.[process],
T1.[exec],
T2.[processid]
FROM
[CTE] AS C
INNER JOIN [Table1] AS T1 ON T1.[exec] = C.[parent]
INNER JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
WHERE
C.[parent] <> C.[process]
)
SELECT DISTINCT C.[process], C.[parent]
FROM [CTE] AS C
WHERE
NOT EXISTS (SELECT 1 FROM [CTE]
WHERE [process] = C.[process] AND [exec] = C.[parent])
AND C.[process] IN (SELECT [process] FROM [Table0] WHERE [name] LIKE '%Aswini%')
I hope this works well enough for you.
Would you try using a cursor and storing the first table in cursor and get the process id by going till the end of the cursor.
DECLARE f_cursor CURSOR FOR
Select Exec, stepid
from Table1
OPEN f_cursor
FETCH NEXT FROM f_cursor
INTO #exec,#stepid
WHILE ##FETCH_STATUS = 0
BEGIN
select b.processid
from Table1 a
inner join Table2 b on a.stepid = b.stepid
where a.condition = 0
and a.exec =#exec
//store the processid somewhere for later use.
END
CLOSE f_cursor;
DEALLOCATE f_cursor;

Copy column in one table to another column in another table

I have three tables and there respective columns
tReferences:
PK_Reference
FK_ReferenceType
ReferenceValue
thePKofMain_FK
tReferencesTypes:
PK_ReferenceType
ReferenceName
tMain:
PK_Main
FirstReferenceValue
SecondReferenceValue
ThirdReferenceValue
Three references have become important enough that they are going to have to go to the tMain table. Let's say the PK of the three references from tReferenceTypes are 321, 654, and 987. I need to copy the references values from tReference to tMain table where each reference has their own columns now, but I have to make sure that I am adding the value from tReference to the correct PK_Main which is suppose to be the same value as the thePKofMain_FK in tReference table, and that I amtReferenceType PK.
I need something like this...
UPDATE tMain
SET
tMain.FirstReferenceValue = (SELECT ReferenceValue FROM tReference WHERE FK_referenceType =321)
FROM tReference
WHERE tReference. thePKofMain_FK = tMain.PK_Main
But I get this messages which makes sense:
Sub query returned more than 1 value. This is not permitted when the
sub query follows =, !=, <, <= , >, >= or when the sub query is used
as an expression. The statement has been terminated.
UPDATE tMain
SET
tMain.FirstReferenceValue = (SELECT ReferenceValue FROM tReference
JOIN tReference on tReference.thePKofMain_FK = tMain.PK_Main
WHERE FK_referenceType =9001649
WHERE FK_referenceType =321)
FROM tReference
WHERE tReference. thePKofMain_FK = tMain.PK_Main
Msg 209, Level 16, State 1, Line 18. Ambiguous column name
'FK_referenceType'. Msg 209, Level 16, State 1, Line 15. Ambiguous
column name 'ReferenceValue'.
OR should I look into doing it in C#?
Some example data:
tReferences:
PK_Reference
123
456
789
FK_ReferenceType:
321
654
987
ReferenceValue
111
a
1
-------------- why I got the first error messages since some reference values are the same
111
c
2
thePKofMain_FK
147
258
369
tReferencesTypes:
PK_ReferenceType
321
654
987
ReferenceName:
FirstReferenceValue
SecondReferenceValue
ThirdReferenceValue
tMain:
PK_Main:
147
258
369
FirstReferenceValue:
Empty
SecondReferenceValue:
Empty
ThirdReferenceValue:
Empty
There is duplicates of ReferenceValues that why the first sql query doesn't work, but even if there are duplicates of the same referenceValue I still need to copy it over to the new table
The second query it accidently typed in WHERE FK_referenceType =9001649 when I was trying to post the question.
Use a Pivot to get the tMain values
INSERT tMain (PK_Main, FirstReferenceValue, SecondReferenceValue, ThirdReferenceValue)
SELECT P.thePKofMain_FK, P.[321], P.[654], P.[987]
FROM (
SELECT FK_ReferenceType, ReferenceValue, thePKofMain_FK
FROM tReference
) T
PIVOT (
MAX(ReferenceValue) FOR FK_ReferenceType IN ([321], [654], [987])
) P

How to set an integer value to one if a record exist in database C# Sql Query

getName_as_Rows is an array which contains some names.
I want to set an int value to 1 if record found in data base.
for(int i = 0; i<100; i++)
{
using (var command = new SqlCommand("select some column from some table where column = #Value", con1))
{
command.Parameters.AddWithValue("#Value", getName_as_Rows[i]);
con1.Open();
command.ExecuteNonQuery();
}
}
I am looking for:
bool recordexist;
if the above record exist then bool = 1 else 0 with in the loop.
If have to do some other stuff if the record exist.
To avoid making N queries to the database, something that could be very expensive in terms of processing, network and so worth, I suggest you to Join only once using a trick I learned. First you need a function in your database that splits a string into a table.
CREATE FUNCTION [DelimitedSplit8K]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(#pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(#pString,t.N,1) = #pDelimiter OR t.N = 0)
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(#pString,s.N1,ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000))
FROM cteStart s
GO
Second, concatenate your 100 variables into 1 string:
"Value1", "Value 2", "Value 3"....
In Sql Server you can just join the values with your table
SELECT somecolumn FROM sometable t
INNER JOIN [DelimitedSplit8K](#DelimitedString, ',') v ON v.Item = t.somecolumn
So you find 100 strings at a time with only 1 query.
Use var result = command.ExecuteScalar() and check if result != null
But a better option than to loop would be to say use a select statement like
SELECT COUNT(*) FROM TABLE WHERE COLUMNVAL >= 0 AND COLUMNVAL < 100,
and run ExecuteScalar on that, and if the value is > 0, then set your variable to 1.

LINQ inner join condition

Suppose I have the following tables:
**Members**
Code Name
001 Sue
002 Peter
003 John
**Sales Info**
MemCode Date Type (A/B) Values
001 17/12/2013 A 100
001 17/11/2013 B 100
002 16/12/2013 A 100
I want to have the following result table
**Member Sales in 2013**
MemCode Jan(A) Jan(B) Feb(A) ... Nov(B) Dec(A) Dec(B)
001 0 0 0 100 100 0
002 0 0 0 0 100 0
I tried to exact some data (The Nov(A) and Nov(B)) first using the query,
var query = from tb in Members
join tb2 in SalesInfo on tb.MemCode equals tb2.MemCode
join tb3 in SalesInfo on tb.MemCode equals tb3.MemCode
where tb2.Type.Equals("A") &&
tb2.Date.Month.Equals(11)
tb3.Type.Equals("B") &&
tb3.Date.Month.Equals(11)
Select ...
However it returns no data as no A record found in November so the whole row is filtered. Is there any suggestion to solve the problem?
The problem is you are asking for (Type==A && Type==B) and it is impossible. You can select A first, and get B value of same date in a subquery.
Indeed, if you want type A and type B, you should select on type == A OR type == B. No row will ever satisfy both conditions :)
Something like this is simpler and probably more effective
var query = from tb in Members
join tb2 in SalesInfo on tb.MemCode equals tb2.MemCode
where (tb2.Type.Equals("A") ||
tb2.Type.Equals("B")) &&
tb2.Date.Month.Equals(11)
Select ...

Categories

Resources