SSRS Recursive Parent gives distinct children only, when children have multiple parents - sql

I have made an SSRS report using the recursive parent functionality to show a hierarchical tree of values. The problem I have is that some children have more than one parent, but because (in order to use the recursive parent nicely) I need to group the results by Id, I only see distinct entries. This means that I only see each child once, even if it "should" appear in multiple locations in the report (under each of its parents).
Here is an example dataset that shows what I mean:
DECLARE #Bear Table
( ParentId INT NOT NULL
,Id INT NOT NULL
,Name VARCHAR(255))
INSERT INTO #Bear
SELECT * FROM
(SELECT 0 AS ParentId, 1 AS Id, 'Daddy Bear' AS Name UNION
SELECT 0 AS ParentId, 2 AS Id, 'Mummy Bear' AS Name UNION
SELECT 1 AS ParentId, 3 AS Id, 'Baby Bear' AS Name UNION
SELECT 2 AS ParentId, 3 AS Id, 'Baby Bear' AS Name) AS FamilyMember
ORDER BY FamilyMember.Id
SELECT * FROM #Bear
My Actual data contains lots of "Baby Bears" where for instance a function is used by more than one procedure, or a procedure is used by more than one report.
When I make the report, I group on Bear.Id, with a recursive parent of Bear.ParentId, which gives me something like this (in the report table):
Level 1 Level 2
Daddy Bear
Baby Bear
Mummy Bear
As you can see, "Baby Bear" only appears once (normally, Id would be unique and this would make perfect sense). What I would like is for SSRS to display is something more like this:
Level 1 Level 2
Daddy Bear
Baby Bear
Mummy Bear
Baby Bear
This would give the users a much better idea of the actual structure they are looking at.
So far, I have tried changing the report group to group by "cstr(Fields!Id.Value) & cstr(Fields!ParentId.Value)", in order to re-establish a unique grouping, so that no records are aggregated into invisibility, but this loses the ordering where children appear immediately after their parent, so I get something like this:
Level 1 Level 2
Daddy Bear
Baby Bear
Baby Bear
Mummy Bear
I have also tried adding ROW_NUMBER() OVER (ORDER BY Id, ParentId) as a new column in the query, to group on that, unquely, but SSRS seems to have a problem with this. The final workaround I am now using is to list only the distinct values as in the first example, but use an Action in each table row to run the report again for each node, on click. This is far from ideal, however.
I have also Googled without result.
I am stuck as to what to do.
Any help would be greatly appreciated - what should I do?
Thanks for your time,
Mark

Why can't you add the ROW_NUMBER() exactly?
SELECT ROW_NUMBER() over (order by parentid) as rn, * FROM
(SELECT 0 AS ParentId, 1 AS Id, 'Daddy Bear' AS Name UNION
SELECT 0 AS ParentId, 2 AS Id, 'Mummy Bear' AS Name UNION
SELECT 1 AS ParentId, 3 AS Id, 'Baby Bear' AS Name UNION
SELECT 2 AS ParentId, 3 AS Id, 'Baby Bear' AS Name) AS FamilyMember
Produces a "unique" id per row for grouping on.
UPDATE
So based on my understanding of your problem, you want a recursive CTE. There are quite a few questions here on SO about them, so between that and that link I encourage you to figure out how to produce a dataset that fits your needs.

Related

How to find table names having ID (primary key) of a certain value in a hierarchy of tables?

I use Oracle 11g and have a massive number of tables representing inheritance, where a base parent table has a primary key NUMBER ID. The subsequent tables inherit from it, representing through the shared primary key NUMBER ID. Let's assume there is a multiple layers of such inheritance.
To have a clear picture, let's work with the following simplified structure and assume the hierarchy is quite complex:
- TABLE FOOD
- TABLE FRUIT
- TABLE CYTRUS
- TABLE ORANGE
- TABLE GREPFRUIT
- TABLE VEGETABLE
- TABLE MEAT
- TABLE BEEF
- TABLE SIRLOIN
- TABLE RIB EYE
- TABLE CHICKEN
This is not taxative, regardless of how dumb the example is, assume such a multi-layered hierarchy using Class Table Inheritance (aka Table Per Type Inheritance).
If you want to insert a record to a table ORANGE having a certain generated ID, there must be inserted records to the parent tables (CYTRUS, FRUIT and FOOD) as well. Assume an ORM engine takes care after this as keeping such consistency would be very complex.
Let's also assume each of the tables in the hierarchy ends with a certain word (let's say FOOD: FRUIT_FOOD, CYTRUS_FOOD etc.) - I didn't include it to the chart above for sake of clarity.
Question: I have found a record in FOOD table with ID = 123 based on certain criteria. Thanks to the hierarchical structure, how do I find what tables contain the record with the very same ID using SQL only? I.e. my goal is to find out what * the lowest type in the hierarchy* the certain ID is related to.
Note: If you have also an answer for a newer version of Oracle, don't hesitate to include it as long as others might find it useful.
Assuming all these tables have a column ID but you may adjust based on the example.
Q1. what tables contain the record with the very same ID using SQL only
You could use a series unions to determine this eg.
SELECT
id,
table_type,
heirarchy_level
FROM (
SELECT ID, 'FOOD', 1 FROM FOOD
UNION ALL
SELECT ID,'FRUIT',2 FROM FRUIT
UNION ALL
SELECT ID,'CYTRUS',3 FROM CYTRUS
UNION ALL
SELECT ID,'ORANGE',4 FROM ORANGE
UNION ALL
SELECT ID,'GREPFRUIT',4 FROM GREPFRUIT
UNION ALL
SELECT ID,'VEGETABLE',2 FROM VEGETABLE
UNION ALL
SELECT ID,'MEAT',2 FROM MEAT
UNION ALL
SELECT ID,'BEEF',3 FROM BEEF
UNION ALL
SELECT ID,'SIRLOIN',4 FROM SIRLOIN
UNION ALL
SELECT ID,'RIBEYE',4 FROM RIBEYE
UNION ALL
SELECT ID,'CHICKEN',3 FROM CHICKEN
) t
WHERE
id = 123
This would return a table with the id=123 but more importantly a table listing all tables where the record was present along with the depth/level in the hierarchy. You could then use MAX or order by to determine the deepest level
Q2. what is the lowest type in the hierarchy the certain ID is related to
This would return only one record with the lowest type
SELECT
id,
table_type,
heirarchy_level
FROM (
SELECT ID, 'FOOD', 1 FROM FOOD
UNION ALL
SELECT ID,'FRUIT',2 FROM FRUIT
UNION ALL
SELECT ID,'CYTRUS',3 FROM CYTRUS
UNION ALL
SELECT ID,'ORANGE',4 FROM ORANGE
UNION ALL
SELECT ID,'GREPFRUIT',4 FROM GREPFRUIT
UNION ALL
SELECT ID,'VEGETABLE',2 FROM VEGETABLE
UNION ALL
SELECT ID,'MEAT',2 FROM MEAT
UNION ALL
SELECT ID,'BEEF',3 FROM BEEF
UNION ALL
SELECT ID,'SIRLOIN',4 FROM SIRLOIN
UNION ALL
SELECT ID,'RIBEYE',4 FROM RIBEYE
UNION ALL
SELECT ID,'CHICKEN',3 FROM CHICKEN
) t
WHERE
id = 123
ORDER BY
heirarchy_level desc
LIMIT 1

Combobox with Column Values based on Field Value

I'm making a combobox, that shows a key identifier, and the summary.
.
So I've got cases where there's an Epic (2), and a Story (3), with the same Parent ID. I've also got cases where there's a story (5) with a parent ID (4), that isn't in the table.
I want to create a two column combobox that looks like this
.
I want the first column, my bound column, to only show each Parent ID once. In cases like Parent ID 2, where there is both an Epic and a Story, I want the second column to show the Summary of the Epic. But in cases of Parent ID 4, where ID 4 doesn't exist on my table as it's own record, I want to have Summary populate with the summary of ID 5.
I'm totally lost on how to do this in SQL.
SELECT table.[Parent ID], First(table.[Summary]) As [Summary]
is about as far as I've gotten, but that just returns the first Summary for a given Parent ID, whether that record is Type Epic or Story.
I've been stumped on this for a while, any help is most appreciated.
Consider:
SELECT ParentID, Summary FROM Table3 WHERE ID IN (
SELECT TOP 1 ID FROM Table3 AS Dupe
WHERE Dupe.ParentID=Table3.ParentID ORDER BY Dupe.Type);
This is where GROUP BY comes into effect.
SELECT ParentID, MAX(Summary)
FROM Table3
GROUP BY ParentID
But that is only part of the picture, because you will lose the 'Once Told Me', as it's alphabetically less than 'The World'.
If you need to see all Summary then this may help:
SELECT ParentID, Summary, MAX(Type) AS [Type], COUNT(*)
FROM Table3
GROUP BY ParentID, Summary
Hmmm, I think the following does what you want by prioritizing the Epic:
select parentid,
nz(max(iif(type = "Epic", summary, null)),
max(summary)
)
from t
group by parentid;

Pick One Row per Unique ID from duplicate records

In Ms.Access 2010, I have a similar query table like one below where its displaying duplicate records. Problem is that even though I have unique ID's, one of the field has different data than other row since I have combined two seperate tables in this query. I just want to display one row per ID and eliminate other rows. It doesn't matter which row I pick. See below:
ID - NAME - FAVCOLOR
1242 - John - Blue
1242 - John - Red
1378 - Mary - Green
I want to just pick any of the the row with same ID. It doesn't matter which row I pick as long as I am displaying one row per ID is what matters.
ID - NAME - FAVCOLOR
1242 - John - Red
1378 - Mary - Green
Use the SQL from your current query as a subquery and then GROUP BY ID and NAME. You can retrieve the minimum FAVCOLOR since you want only one and don't care which.
SELECT sub.ID, sub.NAME, Min(sub.FAVCOLOR)
FROM
(
SELECT ID, [NAME], FAVCOLOR
FROM TABLE1
UNION ALL
SELECT ID, [NAME], FAVCOLOR
FROM TABLE2
) AS sub
GROUP BY sub.ID, sub.NAME;
Note NAME is a reserved word. Bracket that name or prefix it with the table name or alias to avoid confusing the db engine.
Try selecting union without the ALL parameter and see if you get the desired result.
Your new query would look like
"SELECT ID, NAME, FAVCOLOR FROM TABLE1; UNION SELECT ID, NAME, FAVCOLOR FROM TABLE2;"
If you just want the IDs, why is the color in the query? Maybe I'm missing something.
The only thing I could suggest is to use some aggregate function (min, max) to get one color.
Select
id,
name,
max(favcolor)
from (
(select * from table1) t1
union (select * from table2) t2 )t
group by
id,
name

SQL Update - Commit each row in order

Good morning. I'll do my best to explain my question without posting the SQL (it's 650 lines). Let me know if more information is needed.
We have an in-house fulfillment system that is allocating inventory in real time. For allocation to work properly, we need to know how much inventory is available each time a user asks what they should be working on (by loading/reloading their task list). The data would look something like this:
ID ItemID QtyOrdered QtyAvailableAfterAllocation ParentID
1 1234 5 500 NULL
2 1234 15 485 1
3 1234 10 475 2
Currently a while loop is being used to set the QtyAvailableAfterAllocation column. The example above demonstrates the need for the loop. Row 2's QtyAvailableAfterAllocation is dependent on the value of row 1's QtyAvailableAfterAllocation. Row 3 is dependent on row 2 and so on.
This is the (very) simplified version of the logic. It gets infinitely more complicated when you take into account kits (groups of inventory items that belong to a single parent item). There are times that inventory does not need to be allocated to the item because it exists inside of a kit that has sufficient inventory to fulfill the order. This is why we can't do a running total. Also, kits could be nested inside of kits to the Nth level. Therein lies the problem. When dealing with a large amount of orders that have nested kits, the performance of the query is very poor. I believe that the loop is to blame (testing has proved this). So, here's the question:
Is it possible to commit an update, one row at a time and in a specific order (without a loop), so that the child record(s) below can access the updated column (QtyAvailAfterOrder_AllocationScope) in the parent record?
EDIT
Here is a small portion of the SQL. It's the actual while loop. Maybe this will help show the logic that's needed to determine the allocation for each record.
http://pastebin.com/VM9iasq9
Can you cheat and do something like this?
DECLARE #CurrentCount int
SELECT #CurrentCount = QtyAvailableAfterAllocation
FROM blah
WHERE <select the parent of the first row>
UPDATE blah
SET QtyAvailableAfterAllocation = #CurrentCount - QtyOrdered,
#CurrentCount = #CurrentCount - QtyOrdered
WHERE <it is valid to deduct the count>
This should allow you to keep the update as set based and count downwards from a starting quantity. The crux of the problem here is the WHERE clause.
One method we have been doing is to flatten a hierarchy of values (in your case, the Nth kits idea) into a table, then you can join onto this flat table. The flattening of the hierarchy and the single join should help alleviate some of the performance quirks. Perhaps use a view to flatten the data.
Sorry this isn't a direct answer and only ideas.
If you can provide a sample data structure showing how the kits fit in, I'm sure someone can help thrash out a more specific solution.
If you do have requests queued up in some structure, you wouldn't employ a SQL statement to process the queue; "queue" and "SQL", conceptually, are at odds: SQL is set-based, not procedural.
So, forget about using a query to manage the queued requests, and process the queue in a procedure, wrapping each part requisition in a transaction:
pseudo:
WHILE REQUESTS_REMAIN_IN_QUEUE
begin trans
execute requisition SQL statements
commit
LOOP
Your requisition statements (simplified) might look like this:
update inventory
set QOH = QOH- {requested amount}
where partno = ? and QOH >= {requested amount}
insert orderdetail
(customer, orderheaderid, partno, requestedamount)
values
(custid, orderheaderid, partno, requested_amount)
Now in a complicated system involving kits and custom business logic, you might have a rule that says not to decrement inventory if every component in a kit is not avaialable. Then you'd have to wrap your kit requisition in a transaction and rollback if you encounter a situation where an individual component in the kit is backordered, say.
I think this problem can be solved using purely set-based approach.
Basically, you need to perform these steps:
Obtain the table of currently available quantity for every item.
Obtain the running totals from the ordered quantity due to be processed.
Get QtyAvailableAfterAllocation for every item as the result of subtraction of its running total from its available quantity.
Here's a sample solution:
/* sample data definition & initialisation */
DECLARE #LastQty TABLE (Item int, Qty int);
INSERT INTO #LastQty (Item, Qty)
SELECT 0123, 404 UNION ALL
SELECT 1234, 505 UNION ALL
SELECT 2345, 606 UNION ALL
SELECT 3456, 707 UNION ALL
SELECT 4567, 808 UNION ALL
SELECT 5678, 909;
DECLARE #Orders TABLE (ID int, Item int, OrderedQty int);
INSERT INTO #Orders (ID, Item, OrderedQty)
SELECT 1, 1234, 5 UNION ALL
SELECT 2, 1234, 15 UNION ALL
SELECT 3, 2345, 3 UNION ALL
SELECT 4, 1234, 10 UNION ALL
SELECT 5, 2345, 37 UNION ALL
SELECT 6, 2345, 45 UNION ALL
SELECT 7, 3456, 50 UNION ALL
SELECT 8, 4567, 25 UNION ALL
SELECT 9, 2345, 30;
/* the actuall query begins here */
WITH RankedOrders AS (
SELECT
*,
rn = ROW_NUMBER() OVER (PARTITION BY Item ORDER BY ID)
FROM #Orders
),
RunningOrderTotals AS (
SELECT
ID,
Item,
OrderedQty,
RunningTotalQty = OrderedQty,
rn
FROM RankedOrders
WHERE rn = 1
UNION ALL
SELECT
o.ID,
o.Item,
o.OrderedQty,
RunningTotalQty = r.RunningTotalQty + o.OrderedQty,
o.rn
FROM RankedOrders o
INNER JOIN RunningOrderTotals r ON o.Item = r.Item AND o.rn = r.rn + 1
)
SELECT
t.ID,
t.Item,
t.OrderedQty,
QtyAvailableAfterAllocation = oh.Qty - t.RunningTotalQty
FROM RunningOrderTotals t
INNER JOIN #LastQty oh ON t.Item = oh.Item
ORDER BY t.ID;
Note: For the purpose of my example I initialised the available item quantity table (#LastQty) manually. However, you are most probably going to derive it from your data.
Based on the comments/answers above and my inability to accurately represent this complicated issue properly, I've rewritten the processing in C#. Using PLINQ, I've reduced the processing time from 15 seconds to 4. Thanks to all those who tried to help!
If this isn't the appropriate way to close a question, let me know (and let me know the appropriate way so I can do that instead).

how to query with child relations to same table and order this correctly

Take this table:
id name sub_id
---------------------------
1 A (null)
2 B (null)
3 A2 1
4 A3 1
The sub_id column is a relation to his own table, to column ID.
subid --- 0:1 --- id
Now I have the problem to make a correctly SELECT query to show that the child rows (which sub_id is not null) directly selected under his parent row. So this must be a correctly order:
1 A (null)
3 A2 1
4 A3 1
2 B (null)
A normal SELECT order the id. But how or which keyword help me to order this correctly?
JOIN isn't possible I think because I want to get all the rows separated. Because the rows will be displayed on a Gridview (ASP.Net) with EntityDataSource but the child rows must be displayed directly under his parent.
Thank you.
Look at Managing Hierarchical Data in MySQL.
Since recursion is an expensive operation because basicly you're firing multiple queries to your database you could consider using the Nested Set Model. In short you're assigning numbers to ranges in your table. It's a long article but it worth reading it. I've used it during my internship as a solution not to have 1000+ queries, But bring it down to 1 query.
Your handling 'overhead' now lies at the point of updating the table by adding, updating or deleting records. Since you then have to update all the records with a bigger 'right-value'. But when you're retrieving the data, it all goes with 1 query :)
select * from table1 order by name, sub_id will in this case return your desired result but only because the parents names and the child name are similar. If you're using SQL 2005 a recursive CTE will work:
WITH recurse (id, Name, childID, Depth)
AS
(
SELECT id, Name, ISNULL(childID, id) as id, 0 AS Depth
FROM table1 where childid is null
UNION ALL
SELECT table1.id, table1.Name, table1.childID, recurse.Depth + 1 AS Depth FROM table1
JOIN recurse ON table1.childid = recurse.id
)
SELECT * FROM recurse order by childid, depth
SELECT
*
FROM
table
ORDER BY
COALESCE(id,sub_id), id
btw, this will work only for one level.. any thing more than that requires recursive/cte function