Prisma - How to group children together in self-relation query - database

I'm having a following model in my schema:
model Collection {
id String #id #default(cuid())
title String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
parent Collection? #relation("CollectionChildren", fields: [parentId], references: [id])
parentId String?
children Collection[] #relation("CollectionChildren")
}
What I have is a self-relation of collections. They can have unlimited levels.
Now, I want to query and order and list them so that the final result looks like this:
Collection 1
Collection 2
Collection 2 Child 1
Collection 2 Child 2
Collection 2 Child 3
Collection 3
Collection 3 Child 1
Collection 3 Child 1 Sub-child 1
Collection 3 Child 2
Collection 3 Child 3
Collection 4
The problem is that I have no idea how to query then so that they are ordered by title, while also grouped by parent.
Is that even possible in Prisma/Postgres?

It would be difficult to get all the children without knowing the depth of the levels of children (as you mentioned that there could be unlimited levels).
Prisma has a feature request to Support recursive relationships #3725
As a workaround, you would need to use Raw Query.
The SQL query could look something like this:
WITH RECURSIVE collection_tree AS (
SELECT id, title, parentId
FROM Collection
WHERE parentId IS NULL
UNION ALL
SELECT c.id, c.title, c.parentId
FROM Collection c
JOIN collection_tree ct ON ct.id = c.parentId
)
SELECT *
FROM collection_tree
ORDER BY title;
This query uses a recursive CTE to first select the top-level collections (where the parentId is null), and then select all the children of those collections by joining the collection_tree CTE with the Collection table on the parentId. The result is a flattened list of all the collections and their children, ordered by the title field.

Related

How to do Multiple Joins with Merge Into?

I have these 3 tables
Company
id
Branch
id
Items
id
StockNumber
Company can have many branches and a branch can have many items.
Now I got to write a query that will either insert or update an item depending on conditions.
Some items can only appear once in the company and some items can appear in each branch.
The problem for me is the ones that can only appear once in the company. I think I am going to need to basically join all these tables together and do a check but I don't know how to do this join in a "Merge Into Sp"
I made a table type that looks like this
CREATE TYPE ItemTableType AS TABLE
(
BranchId INT,
CompanyId INT
Description nvarchar(Max),
StockNumber: INT
);
In my code I can pass the companyId into my tabletype
CREATE PROCEDURE dbo.Usp_upsert #Source ItemTableType readonly
AS
MERGE INTO items AS Target
using #Source AS Source
ON
// need to somehow look at the companyId so I can then find the right record reguardlesss of which branch it sits in.
Targert.CompanyId = source.CompanyId // can't do this just like this as Item doesn not have reference to company table.
Target.StockNumber = source.StockNumber
WHEN matched THEN
// update
WHEN NOT matched BY target THEN
// insert
Edit
Sample Data
Company
Id Name
1 'A'
2 'B'
Branch
Id name CompanyId
1 'A.1' 1
2 'A.2' 1
3 'B.1' 2
4 'B.2' 3
Item
Id Name StockNumber BranchId
1 Wrench 12345 1
2 Wrench 12345 3
3 Hammer 484814 2
4 Hammer 85285825 4
Now a bulk data is going to be sent into this SP via C# code and looks something like this
DataTable myTable = ...;
// Define the INSERT-SELECT statement.
string sqlInsert = "dbo.usp_InsertTvp"
// Configure the command and parameter.
SqlCommand mergeCommand = new SqlCommand(sqlInsert, connection);
mergeCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = mergeCommand.Parameters.AddWithValue("#Source", myTable);
tvpParam.SqlDbType = SqlDbType.Structured;
tvpParam.TypeName = "dbo.SourceTableType";
// Execute the command.
insertCommand.ExecuteNonQuery();
Now say when an import of records come in and the data looks like this
Wrench (Name), 12345 (StockNumber), 2 (BranchId..they are switching the branch of this item to another branch)
If I would just send this in then if I used BranchId + Stocknumber nothing would be updated and a new record would be inserted what would be wrong as now 2 branches have the same item(based on stockNumber)
If I would just use StockNumber then these 2 records would be updated.
1 Wrench 12345 1
2 Wrench 12345 3
Which is wrong as these records are from 2 different companies. Thus I need to also use the companyId, thus I need to also check the companyId.
EDIT (from comments):
I think I have to do Target Dot something. This is what I came up with so far:
MERGE INTO Items AS Target
using #Source AS Source
ON Source.CompanyID=(
SELECT TOP 1 Companies.Id
FROM Branches
INNER JOIN Companies
ON Branches.CompanyId = Companies.Id
INNER JOIN InventoryItems
ON Branches.Id = Target.BranchId
where Companies.Id = Source.CompanyId
and StockNumber = Source.StockNumber
)
The description of what you need to do is too vague for me to be specific, but you can simply do a query with JOINs as your source. I like to put it in a CTE to make it pretty like so:
WITH cte AS (SELECT query with JOINS)
MERGE INTO items AS Target
using cte AS Source
ON
EDIT: To also do a JOIN on the Target (items) you need to do it in the ON conditions:
WITH cte AS (SELECT query with JOINS)
MERGE INTO items AS Target
using cte AS Source
ON Source.CompanyID=(
SELECT TOP 1 CompanyId
FROM TableWithCompanyId
JOIN Target
ON JoinCondition=true
)...
I know yours involves two tables to get from items to company, but the example above shows you the technique that I believe you are missing.
EDIT 2, based on latest attempt:
Try it this way:
MERGE INTO Items AS Target
using #Source AS Source
ON Source.CompanyID=(
SELECT TOP 1 Companies.Id
FROM Branches
INNER JOIN Companies
ON Branches.CompanyId = Companies.Id
WHERE Branches.Id = Target.BranchId
)
and Target.StockNumber = Source.StockNumber

Order by a child object

The below query pulls the result on Account and uses two inner queries on child objects Monthly_cc__c and Yearly_cc__c. I would like to sort the result by Processing date of the monthly_cc__c. Please help
Select id,
(SELECT Country__c, Current_Month_Active__c,
Distributor__c,Personal_CC_MTD__c, Total_CC_MTD__c,
Processing_Date__c, Total_Active_CC_MTD__c,
Non_Manager_CC_MTD__c,Leadership_CC_MTD__c, Processing_Month__c,
Processing_Year__c, Global_Case_Credits__c,
Eagle_Manager_Global_New_CC__c, Chairmans_Bonus_Global_New_CC__c
FROM Account.Monthly_CCs__r
WHERE Processing_Month__c IN ( 9,8,7) AND Op_Company__c = 'GBR' ),
(SELECT Total_CC_YTD__c, Total_Active_CC_YTD__c, Non_Manager_CC_YTD__c,
Leadership_Qualification_CC_YTD__c,Operating_Company__c
FROM Account.Yearly_CCs__r
WHERE Operating_Company__c= 'GBR' AND Processing_Date__c =2016-01-01)
From Account where ID IN
('001d000001VpPcyAAF','001d000001liZH4AAM','001d000001Q5sflAAB' )
Based on the fact that you want to sort in the SUB query you have two options:
Create a formula field with the field you want to Sort Account__r.Monthly_CC__c then simply add an ORDER BY SOQL clause with the new formula field
Add ORDER BY SOQL clause with Account__r.Monthly_CC__c

Many to many relation hookup in Delphi

How do I do a many to many (master-master) relation in Delphi, I cannot find an example. Only dblookups and master-detail which I understand.
Like a product can belong to one or more categories. Like this table structure:
Product Table
ProductId, ProductName
CategoryTable
CategoryId, CategoryName
Relation table
ProductId, CategoryId
Ideally if you select a record in the product grid the edit of a product in a detail record is started in the right side of the screen. In here you can edit the product properties an ideally you can select via checkboxes one or more categories in a checkbox group/grid.
How do you hook this up with TTable of TQuery components? Is there a way?
Hope you can help!
Sincerely Edward
Some more explaination:
The goal is like:
master [product grid list] Detail [on selected product]
property **A**
property **B**
property **C**
property **D**
property **Category collection**
Category 1 - checked
Category 2 - unchecked
Category 3 - unchecked
Category 4 - checked
This can be transformed to a simple Master-Detail relation by using a joined query over Relation Table and Category Table (similar like this example):
SELECT * FROM Relation a
JOIN Category b on a.CategoryId = b.CategoryId
WHERE ProductId = :ProductId
In case you want a list of all categories where a separate field indicates if a relation to the product exists, you can use a query like this (example for MSSQL, the new field is named Checked):
SELECT c.*, CASE WHEN r.CategoryId IS NULL THEN 0 ELSE 1 END AS Checked
FROM CATEGORY
LEFT JOIN Relation r ON (c.CategoryId = r.CategoryId) AND (r.ProductId = :ProductId)
ORDER BY c.CategoryId
Note that you have to write the code to add or delete the relation record when you manipulate the check list by yourself.

Analysis service create recursive hierarchy

I have following table:
CatId CatName parent CatId
1 Category 1 NULL
2 Category 2 NULL
3 SubCat 1 1
4 SubSubCat 1 3
5 SSSubCat 1 4
In Analysis Service I want to create Hierarchy in dimension such that it allows me to drill down till N Level.. Currently I am able to do it only 2 levels.. Category and Sub Category.. but I would like to go till N level if N level is not possible atleast till 4-5 levels.
The type of Hierarchy you appear to be attempting is called a Parent-Child Dimension. SSAS will use recursive joins to "explode" your data into a tree shape.
But your table as you describe it is a little confusing. So I am offering a solution that requires you to rethink your table a little. A classic Parent-Child will have for each node (record) in the hierarchy:
A key (ID) for the node
The literal text (Name) for the node
A foreign key called the parent
In your example, the column labelled "parent" appears to be superfluous. The last column in your example (called "CatID") is what the Parent of the dimension usually looks like. If you consider that each record in the table is a "child", the parent of the child acts as a pointer back to some record that owns or contains that record. At the highest level in the hierarchy, records will have no Parent so the Parent column is set to NULL.
Rename the second "CatID" to "parent" and remove or rename the original column called "Parent" (you don't need it). If you tweak your table as I suggest, you should check that the highest level is correct by running the following query:
SELECT CatID, CatName, parent FROM mytable WHERE (parent IS NULL)
Then to get the next level down run the following query:
SELECT HighestLevel.CatID, HighestLevel.CatName, HighestLevel.parent, Level2.CatID AS Level2ID, Level2.CatName AS Level2Name
FROM mytable AS HighestLevel
INNER JOIN mytable AS Level2 ON HighestLevel.CatID = Level2.parent
WHERE (HighestLevel.parent IS NULL)
Note the recursive INNER JOIN. Run at least one more query to view another level down to verify that the keys are "expanding" the way you expect:
SELECT HighestLevel.CatID, HighestLevel.CatName, HighestLevel.parent, Level2.CatID AS Level2ID, Level2.CatName AS Level2Name, Level3.CatID AS Level3ID, Level3.CatName AS Level3Name
FROM mytable AS HighestLevel
INNER JOIN mytable AS Level2 ON HighestLevel.CatID = Level2.parent
INNER JOIN mytable AS Level3 ON Level2.CatID = Level3.parent
WHERE (HighestLevel.parent IS NULL)
You could keep adding levels as necessary to convince yourself that the data is correct. This is essentially what SSAS is doing when it builds a Parent-Child hierarchy.
Finally, you'll add this table to the DSV and create a Parent-Child Dimension. That's a bit more complicated and this looks like a great starter article. SSAS will keep adding levels as necessary until it runs out of data.
In AdventureWorks, the Employee dimension has an example of this. Assuming your category is on your fact table:
Set your ParentCatID to be a FK of CatID in the DSV
Reference your Parent attribute as the Parent Attribute type in the Dimension Hierarcy manager
Add the attribute into your hierarchy
The nested levels should be able to be browsed in your Category Hierarchy.

Complex query possible?

I have 3 tables, a parent table and 2 child tables. Lets say Mother_c is the parent. Then Child_c and Pet_c are the 2 child tables that have master-detail relationship pointer to Mother_c.
I have the Id of one row from Child_c, I want to retrieve all the rows from Pet_c that correspond to the Mother_c of that single Child_c row.
I'm wondering if this is possible in one SOQL query?
Yes, this is totally possible with semi-join SOQL. I tested this out with the standard CRM objects like this:
SELECT Id,
(SELECT Id FROM Cases)
FROM Account
WHERE Id IN (SELECT AccountId
FROM Contact
WHERE Id = '0036000000qCwp9'
)
To walk you through this, with a given Contact id, you first find the parent Account, and then traverse back down to the child Cases. In your example with custom objects, it would be very similar, but would use the __r custom relationships names instead:
SELECT Id,
(SELECT Id FROM Pet__r)
FROM Mother__c
WHERE Id IN (SELECT Mother__c
FROM Child__c
WHERE Id = '003a000000qCwp9'
)

Resources