Teradata Stored Procedures can't handle recursive queries - stored-procedures
I have been trying to translate this SP at http://hansolav.net/sql/graphs.html#dijkstra from T-SQL to Teradata SQL. I've gotten most of it done apart from the recursive query. I understand that Teradata SPs can't handle recursive queries. Does anyone know how to re-write recursive query bit to make this SP work?
We are currently using TD 16.
Here is my code
CREATE VOLATILE TABLE VT_Nodes
(
Id INT, -- The Node Id
Estimate DECIMAL(10,3) , -- What is the distance to this node, so far?
Predecessor INT, -- The node we came from to get to this node with this distance.
Done SMALLINT -- Are we done with this node yet (is the estimate the final distance)?
)PRIMARY INDEX(id)
ON COMMIT PRESERVE ROWS;
CREATE PROCEDURE db.usp_Dijkstra
(
StartNode INT
, EndNode INT /*= NULL*/
)
SQL SECURITY INVOKER
BEGIN
/* -- Automatically rollback the transaction if something goes wrong.
SET XACT_ABORT ON
BEGIN TRAN
*/
-- Increase performance and do not intefere with the results.
--SET NOCOUNT ON;
DECLARE FromNode INT;
DECLARE CurrentEstimate INT;
-- Fill the temporary table with initial data
INSERT INTO VT_Nodes (Id, Estimate, Predecessor, Done)
SELECT Id, 9999999.999, NULL, 0 FROM db.T_NODE;
-- Set the estimate for the node we start in to be 0.
UPDATE VT_Nodes SET Estimate = 0 WHERE Id =StartNode;
/* IF ##rowcount <> 1
BEGIN
DROP TABLE VT_Nodes
RAISERROR ('Could not set start node', 11, 1)
ROLLBACK TRAN
RETURN 1
END*/
-- Run the algorithm until we decide that we are finished
label1:
WHILE (1 = 1) DO
BEGIN
-- Reset the variable, so we can detect getting no records in the next step.
SET FromNode = NULL;
-- Select the Id and current estimate for a node not done, with the lowest estimate.
SET FromNode = (SELECT TOP 1 Id FROM VT_Nodes WHERE Done = 0 AND Estimate < 9999999.999 ORDER BY Estimate);
SET CurrentEstimate = (SELECT TOP 1 Estimate FROM VT_Nodes WHERE Done = 0 AND Estimate < 9999999.999 ORDER BY Estimate);
-- Stop if we have no more unvisited, reachable nodes.
IF FromNode IS NULL OR FromNode = EndNode THEN
LEAVE label1;
END IF;
-- We are now done with this node.
UPDATE VT_Nodes
SET Done = 1
WHERE Id = FromNode;
-- Update the estimates to all neighbour node of this one (all the nodes
-- there are edges to from this node). Only update the estimate if the new
-- proposal (to go via the current node) is better (lower).
UPDATE VT_Nodes
FROM db.T_Edge AS e
SET Estimate = CurrentEstimate + e.Weight
, Predecessor = FromNode
WHERE VT_Nodes.Id = e.ToNode
AND e.FromNode = FromNode
AND (CurrentEstimate + e.Weight) < VT_Nodes.Estimate
AND Done = 0
;
END ;
END WHILE label1;
DECLARE CUR1 CURSOR WITH RETURN ONLY TO CLIENT FOR
-- Select the results. We use a recursive common table expression to
-- get the full path from the start node to the current node.
WITH RECURSIVE BacktraceCTE(Id, nm, Distance, PATH, NamePath)
AS
(
-- Anchor/base member of the recursion, this selects the start node.
SELECT n.Id
, nd.nm
, n.Estimate
, CAST(n.Id AS VARCHAR(8000)) AS PATH
,CAST(nd.nm AS VARCHAR(8000)) AS NamePath
FROM VT_Nodes AS n
JOIN db.T_NODE AS nd
ON n.Id = nd.Id
WHERE n.Id = StartNode
UNION ALL
-- Recursive member, select all the nodes which have the previous
-- one as their predecessor. Concat the paths together.
SELECT n.Id
, nd.nm
, n.Estimate
,CAST((cte.PATH || ',' ||TRIM(n.Id)) AS VARCHAR(8000))
,CAST((cte.NamePath || ',' || nd.nm) AS VARCHAR(8000))
FROM VT_Nodes n
JOIN BacktraceCTE AS cte
ON n.Predecessor = cte.Id
JOIN db.T_NODE AS nd
ON n.Id = nd.Id
)
SELECT Id
, nm
, Distance
, PATH
, NamePath
FROM BacktraceCTE
WHERE (Id = EndNode OR EndNode IS NULL) -- This kind of where clause can potentially produce
ORDER BY Id -- a bad execution plan, but I use it for simplicity here.
;
OPEN CUR1;
DROP TABLE VT_Nodes;
/* COMMIT TRAN
RETURN 0*/
END ;
Tables
CREATE TABLE DB.T_NODE
(
Id INT
,Nm VARCHAR(50)
)PRIMARY INDEX(id)
;
CREATE TABLE DB.T_Edge
(
FromNode INT ,
ToNode INT,
Weight DECIMAL (10, 3)
) PRIMARY INDEX(FromNode ,ToNode)
;
DATA
INSERT DB.T_NODE (Id, Nm) VALUES (1, 'Seattle');
INSERT DB.T_NODE (Id, Nm) VALUES (2, 'San Francisco');
INSERT DB.T_NODE (Id, Nm) VALUES (3, 'Las Vegas');
INSERT DB.T_NODE (Id, Nm) VALUES (4, 'Los Angeles');
INSERT DB.T_NODE (Id, Nm) VALUES (5, 'Denver');
INSERT DB.T_NODE (Id, Nm) VALUES (6, 'Minneapolis');
INSERT DB.T_NODE (Id, Nm) VALUES (7, 'Dallas');
INSERT DB.T_NODE (Id, Nm) VALUES (8, 'Chicago');
INSERT DB.T_NODE (Id, Nm) VALUES (9, 'Washington DC');
INSERT DB.T_NODE (Id, Nm) VALUES (10, 'Boston');
INSERT DB.T_NODE (Id, Nm) VALUES (11, 'New York');
INSERT DB.T_NODE (Id, Nm) VALUES (12, 'Miami');
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (1, 2, 1306.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (1, 5, 2161.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (1, 6, 2661.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (2, 1, 1306.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (2, 3, 919.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (2, 4, 629.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (3, 2, 919.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (3, 4, 435.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (3, 5, 1225.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (3, 7, 1983.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (4, 2, 629.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (4, 3, 435.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (5, 1, 2161.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (5, 3, 1225.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (5, 6, 1483.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (5, 7, 1258.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (6, 1, 2661.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (6, 5, 1483.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (6, 7, 1532.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (6, 8, 661.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (7, 3, 1983.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (7, 5, 1258.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (7, 6, 1532.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (7, 9, 2113.000);
INSERT DB.T_Edge (FromNode, ToNode, Weight) VALUES (7, 12, 2161.000);
Unless you run an old Teradata version you can use recursion, but any DECLARE must be done first within a block. There are just some minor modifications needed:
REPLACE PROCEDURE usp_Dijkstra
(
StartNode INT
, EndNode INT /*= NULL*/
)
SQL SECURITY INVOKER
BEGIN
/* -- Automatically rollback the transaction if something goes wrong.
SET XACT_ABORT ON
BEGIN TRAN
*/
-- Increase performance and do not intefere with the results.
--SET NOCOUNT ON;
DECLARE FromNode INT;
DECLARE CurrentEstimate INT;
-- Fill the temporary table with initial data
INSERT INTO VT_Nodes (Id, Estimate, Predecessor, Done)
SELECT Id, 9999999.999, NULL, 0 FROM T_NODE;
-- Set the estimate for the node we start in to be 0.
UPDATE VT_Nodes SET Estimate = 0 WHERE Id =StartNode;
/* IF ##rowcount <> 1
BEGIN
DROP TABLE VT_Nodes
RAISERROR ('Could not set start node', 11, 1)
ROLLBACK TRAN
RETURN 1
END*/
-- Run the algorithm until we decide that we are finished
label1:
WHILE (1 = 1) DO
BEGIN
-- Reset the variable, so we can detect getting no records in the next step.
SET FromNode = NULL;
-- Select the Id and current estimate for a node not done, with the lowest estimate.
-- replacing two SET with a single SELECT
SELECT TOP 1 Id, Estimate INTO FromNode, CurrentEstimate
FROM VT_Nodes WHERE Done = 0 AND Estimate < 9999999.999 ORDER BY Estimate;
-- Stop if we have no more unvisited, reachable nodes.
IF FromNode IS NULL OR FromNode = EndNode THEN
LEAVE label1;
END IF;
-- We are now done with this node.
UPDATE VT_Nodes
SET Done = 1
WHERE Id = FromNode;
-- Update the estimates to all neighbour node of this one (all the nodes
-- there are edges to from this node). Only update the estimate if the new
-- proposal (to go via the current node) is better (lower).
UPDATE VT_Nodes
FROM T_Edge AS e
SET Estimate = :CurrentEstimate + e.Weight
, Predecessor = :FromNode
WHERE VT_Nodes.Id = e.ToNode
AND e.FromNode = :FromNode
AND (:CurrentEstimate + e.Weight) < VT_Nodes.Estimate
AND Done = 0
;
END ;
END WHILE label1;
BEGIN -- wrap the cursor within a new BEGIN/END block
DECLARE CUR1 CURSOR WITH RETURN ONLY TO CLIENT FOR
-- Select the results. We use a recursive common table expression to
-- get the full path from the start node to the current node.
WITH RECURSIVE BacktraceCTE(Id, nm, Distance, Path, NamePath)
AS
(
-- Anchor/base member of the recursion, this selects the start node.
SELECT n.Id
, nd.nm
, n.Estimate
, Cast(n.Id AS VARCHAR(8000)) AS Path
,Cast(nd.nm AS VARCHAR(8000)) AS NamePath
FROM VT_Nodes AS n
JOIN T_NODE AS nd
ON n.Id = nd.Id
WHERE n.Id = StartNode
UNION ALL
-- Recursive member, select all the nodes which have the previous
-- one as their predecessor. Concat the paths together.
SELECT n.Id
, nd.nm
, n.Estimate
,Cast((cte.Path || ',' ||Trim(n.Id)) AS VARCHAR(8000))
,Cast((cte.NamePath || ',' || nd.nm) AS VARCHAR(8000))
FROM VT_Nodes n
JOIN BacktraceCTE AS cte
ON n.Predecessor = cte.Id
JOIN T_NODE AS nd
ON n.Id = nd.Id
)
SELECT Id
, nm
, Distance
, Path
, NamePath
FROM BacktraceCTE
WHERE (Id = EndNode OR EndNode IS NULL) -- This kind of where clause can potentially produce
ORDER BY Id -- a bad execution plan, but I use it for simplicity here.
;
OPEN CUR1;
END;
DROP TABLE VT_Nodes;
/* COMMIT TRAN
RETURN 0*/
END ;
Related
How to ensure both values in a column existis in the table for a particular conditiion?
I have the following structure of tables and columns Table1: bin_master Column1: bin_code values: (B1,B2 etc) Column2: bin_type values: (P, R) Column3: area values: (A1, A2) Table2: area_master Column1: area values: (A1,A2) create table dum_table1_bin_master ( bin_code varchar2(25), bin_type varchar2(1), area varchar2(25) ); create table dum_table2_area_master ( area varchar2(25), area_desc varchar2(25) ); insert into dum_table1_bin_master values ('B1','P','A1'); insert into dum_table1_bin_master values ('B1','R','A1'); insert into dum_table1_bin_master values ('B2','P','A1'); insert into dum_table1_bin_master values ('B2','P','A1'); insert into dum_table1_bin_master values ('B2','R','A2'); insert into dum_table1_bin_master values ('B2','R','A2'); insert into dum_table1_bin_master values ('B3','A','A2'); insert into dum_table1_bin_master values ('B3','A','A2'); insert into dum_table2_area_master values ('A1', 'AREA 1 DESCRIPTION'); insert into dum_table2_area_master values ('A2', 'AREA 2 DESCRIPTION'); Expected Output should be: B1 P A1 B1 R A1 Since the Bin code B1 has both P and R bin_type. In short, how do I write a query to fetch the bin code which have both P and R bin_type for a particular area ? Thanks
Cypher: analog of `sort -u` to merge 2 collections?
Suppose I have a node with a collection in a property, say START x = node(17) SET x.c = [ 4, 6, 2, 3, 7, 9, 11 ]; and somewhere (i.e. from .csv file) I get another collection of values, say c1 = [ 11, 4, 5, 8, 1, 9 ] I'm treating my collections as just sets, order of elements does not matter. What I need is to merge x.c with c1 with come magic operation so that resulting x.c will contain only distinct elements from both. The following idea comes to mind (yet untested): LOAD CSV FROM "file:///tmp/additives.csv" as row START x=node(TOINT(row[0])) MATCH c1 = [ elem IN SPLIT(row[1], ':') | TOINT(elem) ] SET x.c = [ newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) ]; This won't work, it will give an intersection but not a collection of distinct items. More RTFM gives another idea: use REDUCE() ? but how? How to extend Cypher with a new builtin function UNIQUE() which accept collection and return collection, cleaned form duplicates? UPD. Seems that FILTER() function is something close but intersection again :( x.c = FILTER( newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) ) WBR, Andrii
How about something like this... with [1,2,3] as a1 , [3,4,5] as a2 with a1 + a2 as all unwind all as a return collect(distinct a) as unique Add two collections and return the collection of distinct elements. dec 15, 2014 - here is an update to my answer... I started with a node in the neo4j database... //create a node in the DB with a collection of values on it create (n:Node {name:"Node 01",values:[4,6,2,3,7,9,11]}) return n I created a csv sample file with two columns... Name,Coll "Node 01","11,4,5,8,1,9" I created a LOAD CSV statement... LOAD CSV WITH HEADERS FROM "file:///c:/Users/db/projects/coll-merge/load_csv_file.csv" as row // find the matching node MATCH (x:Node) WHERE x.name = row.Name // merge the collections WITH x.values + split(row.Coll,',') AS combo, x // process the individual values UNWIND combo AS value // use toInt as the values from the csv come in as string // may be a better way around this but i am a little short on time WITH toInt(value) AS value, x // might as well sort 'em so they are all purdy ORDER BY value WITH collect(distinct value) AS values, x SET x.values = values
You could use reduce like this: with [1,2,3] as a, [3,4,5] as b return reduce(r = [], x in a + b | case when x in r then r else r + [x] end)
Since Neo4j 3.0, with APOC Procedures you can easily solve this with apoc.coll.union(). In 3.1+ it's a function, and can be used like this: ... WITH apoc.coll.union(list1, list2) as unionedList ...
get child items of a recursive table in sql
I have a table with some level (up to 5 level) as shown below. I want to create a procedure that gets some id and returns the items and their children... I have no idea how to handle it! (when refid is 1, it means that the node is a parent in first level)
DECLARE #Table TABLE( ID INT, ParentID INT, NAME VARCHAR(20) ) INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 1, NULL, 'A' INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 2, 1, 'B-1' INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 3, 1, 'B-2' INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 4, 2, 'C-1' INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 5, 2, 'C-2' DECLARE #ID INT SELECT #ID = 2 ;WITH ret AS( SELECT * FROM #Table WHERE ID = #ID UNION ALL SELECT t.* FROM #Table t INNER JOIN ret r ON t.ParentID = r.ID ) SELECT * FROM ret
Nested select statements
I am trying to return results based on the number of cases if greater than 0 but when i try to execute the stored procedure i get an error stating: Operand type clash: uniqueidentifier is is incompatible with tinyint. And that is for my nested select statement in where clause. SELECT O.OfficeId, O.OfficeName AS Name, AT.Description AS CaseActivity, SUM(A.Duration) AS [CaseMinutes], CAST(SUM(A.Duration) AS FLOAT) / 60 AS [CaseHours], COUNT(A.ActivityId) AS Activities, COUNT(DISTINCT A.CaseId) AS Cases, MIN(CAST(A.Duration AS FLOAT) / 60) AS [Case Min Time], MAX(CAST(A.Duration AS FLOAT) / 60) AS [Case Max Time], SUM(CAST(A.Duration AS FLOAT) / 60) / COUNT(A.ActivityId) AS [Case Avg Time], SUM(CAST(A.Duration AS FLOAT) / 60) AS [Case TotalHours] FROM Activity A INNER JOIN ActivityType AT ON A.ActivityTypeId = AT.ActivityTypeId INNER JOIN ActivityEntry AE ON A.ActivityEntryId = AE.ActivityEntryId INNER JOIN [Case] C ON A.CaseId = C.CaseId INNER JOIN [Office] O ON AE.OfficeId = O.OfficeId INNER JOIN [User] U ON C.CreatedByUserId = U.UserId WHERE A.CaseId in(select A.CaseId from Activity where A.CaseId > 1 AND .dbo.DateOnly(AE.ActivityDate) BETWEEN #BeginDate AND #EndDate) GROUP BY O.OfficeId, O.OfficeName, AT.Description **Desired GOAL from stored procedure** I want to return results from this stored procedure where the case count is greater than 0. Currently this stored procedure returns all activities with cases 0 or greater. I am only interested in getting the activities where the cases are greater than 0. In my where clause i am trying to insert another select statement that will filter the results to cases > 0.
The error message is telling you. One of the comparisons is comparing things from two columns with different types. Look at the table structures and see which columns you're comparing has uniqueidentifier and which have ints and then look at the sql and see which ones you're comparing.
Failed to make query with an explicit join sequence (including alias table) on SQLAlchemy
I have trouble on making a SQLAlchemy query between two tables: ProcessedDataset - ID ProcDSParent - ThisDataset (foreign key to ProcessedDataset.ID) - ItsParent (foreign key to ProcessedDataset.ID) To find the Parent and Child of a Dataset, I need explicit specify the join sequence between: ProcessedDataset <-- ProcDSParent --> ProcessedDataset After make different alias for them, I try different join approach, but still not able to get it. Demonstrate with find Parent of a Dataset: dataset = DB.db_tables[db_alias]['ProcessedDataset'] dsparent = DB.db_tables[db_alias]['ProcDSParent'] parent = dataset.alias('ProcessedDataset_ItsParent') child_parent = dsparent.alias('ThisDataset_ItsParent') keylist = [parent.c.Name] whereclause = dataset.c.Name.op('=')('Test') Approach <1>: FromClause.join + expression.select r_join = dataset r_join.join(child_parent, dataset.c.ID == child_parent.c.ThisDataset) r_join.join(parent, child_parent.c.ItsParent == parent.c.ID) query = select(keylist, from_obj=r_join, whereclause=whereclause) print query SELECT `ProcessedDataset_ItsParent`.`Name` FROM `ProcessedDataset` AS `ProcessedDataset_ItsParent`, `ProcessedDataset` WHERE `ProcessedDataset`.`Name` = %s Approach <2>: orm.join + expression.select join2 = join(dataset, child_parent, dataset.c.ID == child_parent.c.ThisDataset) join2.join(dsparent, child_parent.c.ItsParent == parent.c.ID) print query SELECT `ProcessedDataset_ItsParent`.`Name` FROM `ProcessedDataset` AS `ProcessedDataset_ItsParent`, `ProcessedDataset` INNER JOIN `ProcDSParent` AS `ThisDataset_ItsParent` ON `ProcessedDataset`.`ID` = `ThisDataset_ItsParent`.`ThisDataset` WHERE `ProcessedDataset`.`Name` = %s As you can see, none of them are Parent of a Dataset, which should be: SELECT `ProcessedDataset_ItsParent`.`Name` FROM `ProcessedDataset` INNER JOIN `ProcDSParent` AS `ThisDataset_ItsParent` ON `ProcessedDataset`.`ID` = `ThisDataset_ItsParent`.`ThisDataset` INNER JOIN `ProcessedDataset` AS `ProcessedDataset_ItsParent` ON `ThisDataset_ItsParent`.'ItsParent` = `ProcessedDataset_ItsParent`.`ID` WHERE `ProcessedDataset`.`Name` = %s Appreciate for any help, stuck here for couple of days already! Dong Attaching minimal DDL and python code CREATE TABLE ProcessedDataset ( ID BIGINT UNSIGNED not null auto_increment, Name varchar(500) not null, primary key(ID) ) ; CREATE TABLE ProcDSParent ( ID BIGINT UNSIGNED not null auto_increment, ThisDataset BIGINT UNSIGNED not null, ItsParent BIGINT UNSIGNED not null, primary key(ID), unique(ThisDataset,ItsParent) ) ; ALTER TABLE ProcDSParent ADD CONSTRAINT ProcDSParent_ThisDataset_FK foreign key(ThisDataset) references ProcessedDataset(ID) on delete CASCADE ; ALTER TABLE ProcDSParent ADD CONSTRAINT ProcDSParent_ItsParent_FK foreign key(ItsParent) references ProcessedDataset(ID) on delete CASCADE ; INSERT INTO ProcessedDataset VALUES (0, "ds0"); INSERT INTO ProcessedDataset VALUES (1, "ds1"); INSERT INTO ProcessedDataset VALUES (2, "ds2"); INSERT INTO ProcessedDataset VALUES (3, "ds3"); INSERT INTO ProcessedDataset VALUES (4, "ds4"); INSERT INTO ProcessedDataset VALUES (5, "ds5"); INSERT INTO ProcessedDataset VALUES (6, "ds6"); INSERT INTO ProcessedDataset VALUES (7, "ds7"); INSERT INTO ProcDSParent VALUES (0, 0, 1); INSERT INTO ProcDSParent VALUES (1, 2, 1); INSERT INTO ProcDSParent VALUES (2, 1, 3); INSERT INTO ProcDSParent VALUES (3, 3, 4); INSERT INTO ProcDSParent VALUES (4, 5, 6); INSERT INTO ProcDSParent VALUES (5, 7, 6); (ds0, ds2)-> ds1 -> ds3 -> ds4 (ds5, ds7) -> ds6 from sqlalchemy import Table, create_engine, MetaData from sqlalchemy import and_ from sqlalchemy.sql import select from sqlalchemy.orm import join url = 'mysql://cms:passcms#localhost:3306/testbed' engine = create_engine(url, strategy = 'threadlocal') kwargs = {'autoload':True} db_meta = MetaData() db_meta.bind = engine dataset = Table('ProcessedDataset', db_meta, **kwargs) dsparent = Table('ProcDSParent', db_meta, **kwargs) parent = dataset.alias('ProcessedDataset_ItsParent') child_parent = dsparent.alias('ThisDataset_ItsParent') keylist = [parent.c.Name] whereclause = dataset.c.Name.op('=')('ds0') r_join = dataset r_join.join(child_parent, dataset.c.ID == child_parent.c.ThisDataset) r_join.join(parent, child_parent.c.ItsParent == parent.c.ID) query = select(keylist, whereclause) print query print engine.execute(query).fetchall() query.append_from(r_join) print query print engine.execute(query).fetchall() query = select(keylist, from_obj=r_join, whereclause=whereclause) print query print engine.execute(query).fetchall() join2 = join(dataset, child_parent, dataset.c.ID == child_parent.c.ThisDataset) join2.join(dsparent, child_parent.c.ItsParent == parent.c.ID) query.append_from(join2) print query print engine.execute(query).fetchall() query = select(keylist, from_obj=join2, whereclause=whereclause) print query print engine.execute(query).fetchall()
Michael Bayer Replied my via mail. the join() method returns a new Join object, that's the one which represents the join. The original object is unchanged: r_join = dataset r_join = r_join.join(child_parent, ...) r_join = r_join.join(parent, ...) AND also I tried out the orm.join: join1 = join(child_parent, dataset, child_parent.c.ThisDataset == dataset.c.ID) join2 = parent.join(join1, parent.c.ID == child_parent.c.ItsParent)