get child items of a recursive table in sql - stored-procedures

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

Related

Store the result of sql and process it in informix

We have a view which contains 2 columns: pattern_start_time, pattern_end_time.
The select query in the function will convert it to minutes and using that result we are processing to get the shift unused coverage.The function is getting created but the processing is not happening and getting the below error:
SQLError[IX000]:Routine (my_list) cant be resolved.
Also please enter image description heresuggest to loop till the length of the result.
CREATE function myshifttesting(orgid int) returning int;
DEFINE my_list LIST( INTEGER not null );
DEFINE my_list1 LIST( INTEGER not null );
define i, j, sub, sub1 int;
define total int;
TRACE ON;
TRACE 'my testing starts';
INSERT INTO TABLE( my_list )
select
((extend(current, year to second) + (dots.v_shift_coverage.pattern_start_time - datetime(00:00) hour to minute) - current)::interval minute(9) to minute)::char(10)::INTEGER
from
dots.v_shift_coverage
where
org_guid = orgid;
INSERT INTO TABLE( my_list1 )
select
((extend(current, year to second) + (dots.v_shift_coverage.pattern_end_time - datetime(00:00) hour to minute) - current)::interval minute(9) to minute)::char(10)::INTEGER
from
dots.v_shift_coverage
where
org_guid = orgid;
let sub = 0;
let sub1 = 0;
let total = 0;
for j = 0 to 4
if (my_list(j) < my_list1(j))
then
if (my_list(j + 1) > my_list1(j))
then
let sub = sub + my_list(j + 1) - my_list1(j);
end if;
end if;
end for
if (my_list(0) > my_list1(4))
then
let sub1 = my_list(0) - my_list1(4);
end if;
let total = sub + sub1;
return total;
end function;
The error that you are receiving is because my_list(j) is not valid Informix syntax to access a LIST element. Informix is interpreting my_list(j) as a call to a function named mylist.
You can use a temporary table to "emulate" an array with your logic, something like this:
CREATE TABLE somedata
(
letter1 CHAR( 2 ),
letter2 CHAR( 2 )
);
INSERT INTO somedata VALUES ( 'a1', 'a2' );
INSERT INTO somedata VALUES ( 'b1', 'b2' );
INSERT INTO somedata VALUES ( 'c1', 'c2' );
INSERT INTO somedata VALUES ( 'd1', 'd2' );
INSERT INTO somedata VALUES ( 'e1', 'e2' );
DROP FUNCTION IF EXISTS forloop;
CREATE FUNCTION forloop()
RETURNING CHAR( 2 ) AS letter1, CHAR( 2 ) AS letter2;
DEFINE number_of_rows INTEGER;
DEFINE iterator INTEGER;
DEFINE my_letter1 CHAR( 2 );
DEFINE my_letter2 CHAR( 2 );
-- Drop temp table if it already exists in the session
DROP TABLE IF EXISTS tmp_data;
CREATE TEMP TABLE tmp_data
(
tmp_id SERIAL,
tmp_letter1 CHAR( 2 ),
tmp_letter2 CHAR( 2 )
);
-- Insert rows into the temp table, serial column will be the access key
INSERT INTO tmp_data
SELECT 0,
d.letter1,
d.letter2
FROM somedata AS d
ORDER BY d.letter1;
-- Get total rows of temp table
SELECT COUNT( * )
INTO number_of_rows
FROM tmp_data;
FOR iterator = 1 TO number_of_rows
SELECT d.tmp_letter1
INTO my_letter1
FROM tmp_data AS d
WHERE d.tmp_id = iterator;
-- Check if not going "out of range"
IF iterator < number_of_rows THEN
SELECT d.tmp_letter2
INTO my_letter2
FROM tmp_data AS d
WHERE d.tmp_id = iterator + 1;
ELSE
-- iterator + 1 is "out of range", return to the beginning
SELECT d.tmp_letter2
INTO my_letter2
FROM tmp_data AS d
WHERE d.tmp_id = 1;
END IF;
RETURN my_letter1, my_letter2 WITH RESUME;
END FOR;
END FUNCTION;
-- Running the function
EXECUTE FUNCTION forloop();
-- Results
letter1 letter2
a1 b2
b1 c2
c1 d2
d1 e2
e1 a2
5 row(s) retrieved.

How to use a numeric parameter in a stored procedure?

Good morning,
I'm having troubles to integrate a working query into a stored procedure.
My main issue, is that I'm using a WHILE loop with an integer, and that the stored procedure is having troubles with it.
My working query/code is the following:
CREATE TABLE #tempScaffStandingByTime
(
TotalStanding INT,
MonthsAgo INTEGER
)
DECLARE #StartDate DATETIME = null
DECLARE #months INTEGER = 12
use [Safetrak-BradyTechUK]
WHILE #months >= 0
BEGIN
SET #StartDate = DATEADD(mm, -12 + #months, DATEADD(mm, 0, DATEADD(mm,
DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, #months
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > #StartDate)
group by a.uid
SET #months -= 3
END
SELECT * FROM #tempScaffStandingByTime
DROP TABLE #tempScaffStandingByTime
This results in the input which I want:
I then tried to import this code into my stored procedure
DECLARE #Query varchar (8000)
, #Account varchar (100) = 'BradyTechUK'
SET #Account = 'USE [Safetrak-' + #Account + ']'
/************************************************************************************/
/********** Create Table to hold data ***********************************************/
CREATE TABLE #tempScaffStandingByTime
(
TotalStanding INT,
MonthsAgo INTEGER
)
/************************************************************************************/
/********** Populate temp table with data *******************************************/
DECLARE #StartDate DATETIME = null
DECLARE #months INTEGER = 12
SET #Query= +#Account+ '
WHILE '+#months+' >= 0
BEGIN
SET '+#StartDate+' = DATEADD(mm, -12 + ('+#months+', DATEADD(mm, 0, DATEADD(mm, DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, '+#months+'
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > '+#StartDate+')
group by a.uid
SET '+#months+' -= 3
END'
EXEC (#Query)
/************************************************************************************/
/********** Select Statement to return data to sp ***********************************/
Select TotalStanding
, MonthsAgo
FROM #tempScaffStandingByTime
/************************************************************************************/
DROP TABLE #tempScaffStandingByTime
But when loading the stored procedure in SSRS I get a conversion error.
I can convert my parameter to a string, but then it won't count anymore.
I searched far and wide on the internet, tried various things, but can't get it to work.
Any help is appreciated, thanks!
It looks like your problem original comes from the lack of conversion between the INT in #months and your VARCHAR in #Accounts and #Query.
You need to cast the months to a string to append it, otherwise the SQL Server thinks you're trying to do addition.
However you'll also have problems with the usage of your #StartDate variable as you try to set it via your dynamic SQL which is not going to work like that - and as with #months it'll have an incorrect datatype, but I'm also unsure why it's written as is, and what you're tying to achieve with it
I think - from what you're writing - that this might be what you're looking for?
SET #Query= #Account + '
DECLARE #StartDate DATETIME = null
DECLARE #months VARCHAR(2) = ''12''
WHILE #months >= 0
BEGIN
SET #StartDate = DATEADD(mm, -12 + #months, DATEADD(mm, 0, DATEADD(mm, DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, #months, #StartDate
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > #StartDate)
group by a.uid
SET #months -= 3
END'
EXEC (#Query)

Teradata Stored Procedures can't handle recursive queries

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 ;

Dynamic order by multiple columns in SQL Server

I use SQL Server 2012. I have a sample table named 'Table1' with seven columns.
CREATE TABLE TABLE1
(
Field1 INT ,
Field2 INT ,
Field3 INT ,
Field4 INT ,
Field5 INT ,
Field6 INT ,
Field7 INT
)
GO
INSERT INTO TABLE1 VALUES (1,2,9,5,1,5,85)
INSERT INTO TABLE1 VALUES (2,6,8,4,1,4,45)
INSERT INTO TABLE1 VALUES (3,5,7,3,5,6,1)
INSERT INTO TABLE1 VALUES (4,4,6,1,51,4,1)
INSERT INTO TABLE1 VALUES (5,5,5,4,7,2,7)
INSERT INTO TABLE1 VALUES (6,5,4,6,4,7,8)
INSERT INTO TABLE1 VALUES (7,12,5,3,2,5,3)
INSERT INTO TABLE1 VALUES (8,1,6,5,9,5,1)
INSERT INTO TABLE1 VALUES (9,1,13,2,1,7,3)
INSERT INTO TABLE1 VALUES (10,6,9,3,6,2,6)
INSERT INTO TABLE1 VALUES (11,2,1,2,8,7,7)
INSERT INTO TABLE1 VALUES (12,7,6,1,3,3,2)
INSERT INTO TABLE1 VALUES (13,7,2,6,4,7,1)
GO
I have created the below Stored Procedure, This SP is able to query data considering the asked order by column.
I can query my table with each possibility of single column and kind of order by (ASC or Desc).
CREATE Procedure ProceName
(
#OrderByField INT = 1,
#OrderDirection INT = 0 -- 0 = Asc , 1 = Desc
)
As
Begin
SELECT
*
FROM Table1
ORDER BY
CASE WHEN #OrderDirection=0 AND #OrderByField=1 THEN Field1 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=2 THEN Field2 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=3 THEN Field3 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=4 THEN Field4 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=5 THEN Field5 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=6 THEN Field6 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=7 THEN Field7 END ASC,
CASE WHEN #OrderDirection=1 AND #OrderByField=1 THEN Field1 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=2 THEN Field2 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=3 THEN Field3 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=4 THEN Field4 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=5 THEN Field5 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=6 THEN Field6 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=7 THEN Field7 END DESC End
GO
EXECUTE ProceName #OrderByField=1, #OrderDirection=0
EXECUTE ProceName #OrderByField=6, #OrderDirection=1
Now I need to change this sp for accepting multi columns as order by series columns. They can be pass by name or by order of columns.
In this case i should be able to execute my SP like below command:
EXECUTE ProceName #OrderByField='6,7,2', #OrderDirection='0,1,1'
How can I achive this gool with out using the sp_executesql (Dynamic Query)?
Well, your ascending and descending wouldn't matter in your case unless you were trying to choose asc and desc for each column. Here is using the last column.
DECLARE #OrderByField VARCHAR(64) = '1,3,7'
DECLARE #DynamicColumns VARCHAR(256) = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#OrderByField,1,'FIELD1'),2,'FIELD2'),3,'FIELD3'),4,'FIELD4'),5,'FIELD5'),6,'FIELD6'),7,'FIELD7')
DECLARE #OrderDirection INT = 1
DECLARE #order varchar(4)
SET #order = CASE WHEN #OrderDirection = 1 THEN 'DESC' ELSE 'ASC' END
--uncomment this line of code to add the sort to every column which only matters for DESC since ASC is default
--SET #DynamicColumns = REPLACE(#DynamicColumns,',', ' ' + #order + ',')
DECLARE #sql VARCHAR(MAX) = (
'SELECT
*
FROM Table1
ORDER BY ' + #DynamicColumns + ' ' + #order)
SELECT #sql
EXEC(#sql)
CREATE Procedure ProceName
(
#OrderByField VARCHAR(100),
#OrderDirection VARCHAR(100),
)
As
Begin
Declare #SQL VARCHAR(MAX)
if OBJECT_ID('Example1') is not null
begin
drop table Example
end
if OBJECT_ID('Example2') is not null
begin
drop table Example2
end
Create table Example1
(
id int identity(1,1),
Field varchar(20)
)
Create table Example2
(
id int identity(1,1),
OrderNumber varchar(10)
)
--iterate each element for both #OrderByField and #OrderDirection
Declare #separator char(1)=','
Declare #position int = 0
Declare #name varchar(20)
Set #OrderByField = #OrderByField + #separator
---------------------------------------------------------------------------
/*iterate each for #OrderByField */
---------------------------------------------------------------------------
While CHARINDEX (#separator,#OrderByField,#position) != 0
BEGIN
SET #name = SUBSTRING (#OrderByField,#position,CHARINDEX (#separator,#OrderByField,#position)-#position)
SET #SQL = 'Insert into Example1([Field]) Values(' + char(39) + #name + char(39) + ')'
EXEC(#SQL)
SET #position = CHARINDEX(#separator,#OrderByField,#position)+1
END
---------------------------------------------------------------------------
/*iterate each for #OrderDirection */
---------------------------------------------------------------------------
SET #position = 0 --do not forget to reset the position number
Set #OrderDirection = #OrderDirection + #separator
While CHARINDEX (#separator,#OrderDirection,#position) != 0
BEGIN
SET #name = SUBSTRING (#OrderDirection,#position,CHARINDEX (#separator,#OrderDirection,#position)-#position)
SET #SQL = 'Insert into Example2([OrderNumber]) Values(' + char(39) + #name + char(39)+ ')'
EXEC(#SQL)
SET #position = CHARINDEX(#separator,#OrderDirection,#position)+1
END
Set #name = '' --reset the #name for the use of Cursor
declare #NewName varchar(500) =''
Declare row_cursor CURSOR
FOR
select 'Field'+a.Field + ' '+ case when b.OrderNumber = 0 then 'ASC' else 'DESC' end +',' as command
from Example1 as a
inner join Example2 as b
on b.id = a.id
OPEN row_cursor
FETCH NEXT FROM row_cursor into #name
WHILE (##FETCH_STATUS =0)
begin
Set #NewName = #NewName + #name
FETCH NEXT FROM row_cursor into #name
end
close row_cursor
deallocate row_cursor
SET #NewName = REVERSE(STUFF(REVERSE(#NewName),1,1,''))
SET #SQL = 'Select * From Table1 Order by ' + #NewName
--Print(#SQL)
EXEC (#SQL)
END

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)

Resources