SSRS Grouping in Tablix - reporting-services

I need help in generating a tablix using same dataset but should be displayed separately based on dropdown input selection( multi values). Currently i am working on Tablix please let me know if i can use anything else to achieve the output.
Here is my example where i have dropdown with multiple input selection and when i select two students i am getting results into one table instead i want to separate into two tables based on studentID.
CREATE TABLE #tmpStudent( ID INT IDENTITY(1,1), Name varchar(150))
INSERT INTO #tmpStudent(Name)
VALUES('John') ,
('Mike'),
('Albert')
CREATE TABLE #tmpDepartment (ID INT
IDENTITY(1,1), StudentID INT, DepartmentName VARCHAR(100))
INSERT INTO #tmpDepartment ( StudentID, DepartmentName)
VALUES(1, 'Computers'),
(2, 'Computers'),
(1, 'Science'),
(3, 'Mechanical')
DECLARE #xStudent VARCHAR(30) ='John,Mike'
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = 'SELECT s.ID, s.Name, d.DepartmentName
FROM #tmpStudent s
INNER JOIN #tmpDepartment d
ON s.ID = d.StudentID
WHERE s.Name IN('''+ Replace(#xStudent, ',', ''',''') + ''')
ORDER BY s.ID'
--PRINT #Sql
exec sp_executesql #Sql
DROP TABLE #tmpStudent, #tmpDepartment
Current Output
ID Name DepartmentName
1 John Computers
1 John Science
2 Mike Computers
Below is the excepted output where i have to repeat the same tablix based on StudentID(based on multiple input selection in dropdownlist)
ID Name DepartmentName
1 John Computers
1 John Science
ID Name DepartmentName
2 Mike Computers

Related

Copying data in two tables with foreign keys

I have two tables, reviews and grade.
Reviews table has
id_review (primary key), id_lang, email, text etc.
Example
1, 2, email#email.com, test text
2, 2, email#email.com, test text
4, 2, email#email.com, test text
Grade table has
id_review (primary/foreign key), id_criterion, grade
1, 3, 5.00
1, 1, 4.00
2, 3, 3.00
2, 1, 5.00
4, 2, 3.00
I need to copy all the reviews with lang id 2, change the text and the lang id to 1 (this I can do manually).
But as the id_review changes with the copied reviews, I need to create new rows on the grade table, too. Is there a way to make sure that the foreign keys are matched with the copied reviews, too?
I tried to do it the old fashioned way with copy/paste on csv but as some reviews are removed from the reviews table and some reviews have differences in id_criterion count, it's very hard to do for a large table.
Or should I try to edit the table to allow the reviews table to have distinct values for id_lang with the same id_review?
You can create temporary tables (no foreign keys) out of the original ones, patch and validate the data until you satisfy and then insert back to the original tables.
I am not sure how you populate id_review, so I assume they are auto generated when you insert new rows.
create table reviews_temp_20211119 as
select r.id_review as old_id_review
, 0 as new_id_review
, row_number() over(order by r.id_review) as ref_patch_id
, r.id_lang
, r.email
, r.text
from reviews r
where id_lang = 2;
create table grades_temp_20211119 as
select g.id_review
, g.id_criterion
, g.gradate
, 0 as new_id_review
from grades g
where g.id_review in (select t.old_id_review from reviews_temp_20211119 t);
update reviews_temp_20211119
set id_lang = 1;
alter table reviews add column ref_patch_id bigint null;
-- insert back to original to get the auto generated id_review
-- if you use other strategies to populate the id_review, you can do update it directly to the temp table and review if all the data are correct before insert back to the original table
insert into reviews (id_lang, email, text, ref_patch_id)
select id_lang, email, text, ref_patch_id
from reviews_temp_20211119;
update reviews_temp_20211119 t
join reviews r on (r.ref_patch_id = t.ref_patch_id)
set t.new_id_review = r.id_review;
update grades_temp_20211119 g
join reviews_temp_20211119 t on (g.id_review = t.old_id_review)
set g.new_id_review = t.new_id_review);
insert into grades (id_review, id_criterion, grade)
select t.new_id_review
, t.id_criterion
, t.grade
from grades_temp_20211119 t;
By keeping the temporary tables, you have opportunity to review or rollback the change if something went wrong by looking back at the temporary tables.
For a repeatable process, I think a stored procedure with cursors is the way. Here's my version, it accept two parameters, the old idLang you wish to copy, and the new idLang:
CREATE PROCEDURE copyReviewWithNewLang(IN oldidLang INT, IN newidLang INT)
BEGIN
DECLARE c_idReview, c_maxIdReview INT;
DECLARE c_text, c_email VARCHAR;
DECLARE old_c_idreview INT DEFAULT 0;
-- first cursor gets all the review rows of the old language, ordered
DECLARE rev_cur CURSOR FOR SELECT idReview, email, text FROM reviews WHERE id_lang = oldidLang ORDER BY idReview ASC;
-- second cursor gets the highest idReview
DECLARE maxid_cur CURSOR FOR SELECT MAX(idReview) FROM reviews;
-- needed for ending the loop on end of retrieved data
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN rev_cur;
retrieving : LOOP
FETCH rev_cur INTO c_idReview, c_email, c_text;
-- ending the loop
IF done THEN
LEAVE retrieving;
END IF;
IF (old_c_idreview = 0) OR (old_c_idreview != c_idReview) THEN
OPEN maxid_cur;
FETCH maxid_cur INTO c_maxIdReview;
CLOSE maxid_cur;
SET c_maxIdReview = c_maxIdReview + 1
END IF;
-- copying the review row
INSERT INTO reviews (id_review, id_lang, email, text)
VALUES(c_maxIdReview, newidLang, c_email, c_text)
-- copying the grade rows
INSERT INTO grades (id_review, id_criterion, grade)
SELECT c_maxIdReview, id_criterion, grade FROM grades
WHERE id_review = c_idReview;
-- needed for checking if id changed
SET old_c_idreview = c_idReview;
END LOOP;
CLOSE rev_cur;
END;

Link one item to other item in same table

I searched a lot but found nothing.
My scenario is:
I have database with two tables table_item and table_item_linked. table_item has many items. User will come and add item(s). Later other user come and link one item with other item(s) via a form with two dropdown.
What I did so far is:
Structure of table_item:
+-------------------+
| table_item |
+-------------------+
| item_id (Primary) |
| others |
| .... |
| .... |
| .... |
+-------------------+
Structure of table_item_linked:
+---------------------+
| table_item_linked |
+---------------------+
| linked_id | (Primary)
| item_id | (Foreign key referencing -> item_id of table_item)
| linked_items | (here I need to store ids of linked items)
| linked_by | (referencing to user_id of user_table)
| linked_timestamp | (timestamp)
+---------------------+
If I have items in table_item like:
A B C D E F G H
When I link D with G
I can successfully fetch G when I am fetching D or vice versa. But problem came when I
Link H with G
So I must fetch D H while fetching G.
(D H G are linked in all means and upon fetching one, the remaining two must be attached and fetched)
It is like a multiple relation (Many to Many relationship).
Guys I know there must be professional way to do it. I will like to have any guidance. I can even change my database structure.
PS:
Please don't suggest to add #tag as one item is exactly similar to the other linked.
UPDATES
Frontend looks like this. If I intend to link two records I will have two dropdowns as shown:
And If I check details of record A
And If I check details of record B
And If I check details of record C
Assuming your table_item looks like this:
create table table_item (
item_id int unsigned auto_increment not null,
record varchar(50),
primary key (item_id)
);
insert into table_item (record) values
('Record A'),
('Record B'),
('Record C'),
('Record D'),
('Record E'),
('Record F'),
('Record G'),
('Record H');
table_item_linked could then be
create table table_item_linked (
linked_id int unsigned auto_increment not null,
item1_id int unsigned not null,
item2_id int unsigned not null,
linked_by int unsigned not null,
linked_timestamp timestamp not null default now(),
primary key (linked_id),
unique key (item1_id, item2_id),
index (item2_id, item1_id),
foreign key (item1_id) references table_item(item_id),
foreign key (item2_id) references table_item(item_id)
);
This is basically a many-to-many relation between items of the same type.
Note that you usually don't need an AUTO_INCREMENT column here. You can remove it, and define (item1_id, item2_id) as PRIMARY KEY. And linked_by should be a FOREGN KEY referencing the users table.
If a user (with ID 123) wants to link "Record A" (item_id = 1) with "Record B" (item_id = 2) and "Record B" (item_id = 2) with "Record C" (item_id = 3), your INSERT statements would be:
insert into table_item_linked (item1_id, item2_id, linked_by) values (1, 2, 123);
insert into table_item_linked (item1_id, item2_id, linked_by) values (2, 3, 123);
Now - When the user selects "Record A" (item_id = 1), you can get all related items with a recursive query (Requires at least MySQL 8.0 or MariaDB 10.2):
set #input_item_id = 1;
with recursive input as (
select #input_item_id as item_id
), rcte as (
select item_id from input
union distinct
select t.item2_id as item_id
from rcte r
join table_item_linked t on t.item1_id = r.item_id
union distinct
select t.item1_id as item_id
from rcte r
join table_item_linked t on t.item2_id = r.item_id
)
select i.*
from rcte r
join table_item i on i.item_id = r.item_id
where r.item_id <> (select item_id from input)
The result will be:
item_id record
———————————————————
2 Record B
3 Record C
db-fiddle
In your application you would remove set #input_item_id = 1; and change select #input_item_id as item_id using a placeholder to select ? as item_id. Then prepare the statement and bind item_id as parameter.
Update
If the server doesn't support recursive CTEs, you should consider to store redundat data in a separate table, which is simple to query. A closure table would be an option, but it's not necessery here, and might consume too much storage space. I would group items that are connected together (directly and indirectly) into clusters.
Given the same schema as above, we define a new table table_item_cluster:
create table table_item_cluster (
item_id int unsigned not null,
cluster_id int unsigned not null,
primary key (item_id),
index (cluster_id, item_id),
foreign key (item_id) references table_item(item_id)
);
This table links items (item_id) to clusters (cluster_id). Since an item can belong only to one cluster, we can define item_id as primary key. It's also a foreign key referencing table_item.
When a new item is created, it's not connected to any other item and builds an own cluster. So when we insert a new item, we need also to insert a new row in table_item_cluster. For simplicity we identify the cluster by item_id (item_id = cluster_id). This can be done in the application code, or with the following trigger:
delimiter //
create trigger table_item_after_insert
after insert on table_item
for each row begin
-- create a new cluster for the new item
insert into table_item_cluster (item_id, cluster_id)
values (new.item_id, new.item_id);
end//
delimiter ;
When we link two items, we simply merge their clusters. The cluster_id for all items from the two merged clusters needs to be the same now. Here I would just take the least one of two. Again - we can do that in application code or with a trigger:
delimiter //
create trigger table_item_linked_after_insert
after insert on table_item_linked
for each row begin
declare cluster1_id, cluster2_id int unsigned;
set cluster1_id = (
select c.cluster_id
from table_item_cluster c
where c.item_id = new.item1_id
);
set cluster2_id = (
select c.cluster_id
from table_item_cluster c
where c.item_id = new.item2_id
);
-- merge the linked clusters
update table_item_cluster c
set c.cluster_id = least(cluster1_id, cluster2_id)
where c.item_id in (cluster1_id, cluster2_id);
end//
delimiter ;
Now - When we have an item and want to get all (directly and indirectly) linked items, we just select all items (except of the given item) from the same cluster:
select i.*
from table_item i
join table_item_cluster c on c.item_id = i.item_id
join table_item_cluster c1
on c1.cluster_id = c.cluster_id
and c1.item_id <> c.item_id -- exclude the given item
where c1.item_id = ?
db-fiddle
The result for c1.item_id = 1 ("Record A") would be:
item_id record
———————————————————
2 Record B
3 Record C
But: As almost always when dealing with redundant data - Keeping it in sync with the source data can get quite complex. While it is simple to add and merge clusters - When you need to remove/delete an item or a link, you might need to split a cluster, which may require writing recursive or iterative code to determine which items belong to the same cluster. Though a simple (and "stupid") algorithm would be to just remove and reinsert all affected items and links, and let the insert triggers do theit work.
Update 2
Last but not least: You can write a stored procedure, which will iterate through the links:
delimiter //
create procedure get_linked_items(in in_item_id int unsigned)
begin
set #ids := concat(in_item_id);
set #ids_next := #ids;
set #sql_tpl := "
select group_concat(distinct id order by id) into #ids_next
from (
select item2_id as id
from table_item_linked
where item1_id in ({params_in})
and item2_id not in ({params_not_in})
union all
select item1_id
from table_item_linked
where item2_id in ({params_in})
and item1_id not in ({params_not_in})
) x
";
while (#ids_next is not null) do
set #sql := #sql_tpl;
set #sql := replace(#sql, '{params_in}', #ids_next);
set #sql := replace(#sql, '{params_not_in}', #ids);
prepare stmt from #sql;
execute stmt;
set #ids := concat_ws(',', #ids, #ids_next);
end while;
set #sql := "
select *
from table_item
where item_id in ({params})
and item_id <> {in_item_id}
";
set #sql := replace(#sql, '{params}', #ids);
set #sql := replace(#sql, '{in_item_id}', in_item_id);
prepare stmt from #sql;
execute stmt;
end//
delimiter ;
To get all linked items of "Record A" (item_id = 1), you would use
call get_linked_items(1);
db-fiddle
To explain it in pseudo code:
Initialize #ids and #ids_next with the input parameter
Find all item IDs which are directly linked to any ID in #ids_next except of those, which are already in #ids
Store the result into #ids_next (overwrite it)
Append IDs from #ids_next to #ids (merge the two sets into #ids)
If #ids_next is not empty: GOTO step 2.
Return all items with IDs in #ids
The obvious solution is to store one row for each link in table_item_linked.
Your table then becomes
+---------------------+
| table_item_linked |
+---------------------+
| linked_id | (Primary
| from_item_id | (The item linked _from_ -> item_id of table_item)
| to_item_id | the item linked _to_
| linked_by | (referencing to user_id of user_table)
| linked_timestamp | (timestamp)
+---------------------+
In your example, the data would be:
linked_id from_item_id to_item_id linked_by linked_timestamp
------------------------------------------------------------------------
1 D H sd '1 jan 2020'
2 H G sa '2 Jan 2020'
You then need to write a hierarchical query to retrieve all the "children" of G.

How to update all columns and rows from another table with a stored procedure

I have a few tables for a college.
Apply (sID int(3), cName varchar(20), major varchar(20), decision char(1))
College (cName char(20), state char(2), enrollment int(11))
Student (sid char(6), sName char(20), GPA decimal(3,2), sizeHS int(11))
I created this table:
CollegeStats (cName varchar(20), appCount int(11), minGPA decimal(3,2), maxGPA decimal(3,2))
I need to create a stored procedure that updates CollegeStats, with no parameters based on Apply, College, and Student table.
I've worked on several iterations of code to try to come to an answer, and this is my latest one. I'd really appreciates any and all help.
No parameter for this process.
ROUTINE NAME: updateCollegeStatsAll
BEGIN
UPDATE CollegeStats
SET appCount = (SELECT COUNT(*) FROM Apply),
minGPA = (SELECT MIN(GPA) FROM Student),
maxGPA = (SELECT MAX(GPA) FROM Student);
END
When I run this code it updates all the rows to be the same.
cName appCount minGPA maxGPA
-----------------------------------
Cornell 20 2.90 4.00
MIT 20 2.90 4.00
CALTEC 20 2.90 4.00
Davis 20 2.90 4.00
Data should only live in 1 place in the universe. Having a stored proc summarize data is a bad idea. A view is more appropriate for this problem.
All tables should have an ID column. Not sure from your question how the tables relate to each other. But if you had ID columns this is how your view would look:
CREATE VIEW vCollageStats AS
(
SELECT c.cName,
COUNT(DISTINCT a.Id) AS AppCount,
MIN(s.GPA) AS MinGPA,
MAX(s.GPA) AS MaxGPA
FROM College c
LEFT JOIN Apply a
ON c.Id = a.CollegeId
LEFT JOIN Student s
ON a.StudentId = s.Id
GROUP BY c.cName,
)
Maybe you can try this.
/* Create a dummy table to store student and their respective college id */
DECLARE #stud_clg TABLE (sid char(6),GPA decimal(3,2),cName varchar(20))
INSERT INTO #stud_clg
SELECT A.sid, A.GPA, B.cName FROM
Student A INNER JOIN Apply B
ON A.sid = B.sID
/* Create a dummy table to store college and their student count */
DECLARE #clgstudct TABLE (cName varchar(20), ct int(11))
INSERT INTO #clgstudct
SELECT cName,COUNT(*) FROM #stud_clg
GROUP BY cNAME
/* Update your actual table using WHILE loop */
DECLARE #ctrow int = (SELECT COUNT(*) FROM #clgstudct)
DECLARE #studct int(11)
DECLARE #clgname varchar(20)
/* Begin While loop */
WHILE #ctrow != 0
BEGIN
SET #studct = (SELECT TOP 1 ct FROM #clgstudct)
SET #clgname = (SELECT TOP 1 cNAME FROM #clgstudct)
DELETE FROM #clgstudct WHERE cName = #clgname
/* Update statement */
UPDATE CollegeStats SET
appCount = #studct,
minGPA = (SELECT MIN(GPA) FROM #stud_clg WHERE cName = #clgname),
maxGPA = (SELECT MAX(GPA) FROM #stud_clg WHERE cName = #clgname)
WHERE cName = #clgname
/* End Update statement */
SET #ctrow = (SELECT COUNT(*) FROM #clgstudct)
END
/* End While loop */
Because I don't have actual record for your table (and I am too lazy to insert dummy data to test), I code this without using any compiler and just based on my logic. I hope it works for you. If any problem faced, please leave a comment, I will try to fix it. This approach might seem too long, I think there should be a simpler solution.
Additionally, I think that this kind of logic should be done using programming language like PHP, VB or C# instead of using SQL. As an example, you can call an UPDATE query inside your PHP loop for every colleges.

How to compare multiple parameters of a row column value?

how to write query for following request?
my table:
id designation
1 developer,tester,projectlead
1 developer
1 techlead
if id=1,designation="'developer'"
Then need to first,second records.Because 2 rows are having venkat.
if id=1,designation="'developer','techlead'" then need to get 3 records as result.
i wrote one service for inserting records to that table .so that i am maintaining one table to store all designation with same column with comas.
By using service if user pass id=1 designation="'developer','techlead'" then need to pull the above 3 records.so that i am maintaining only one table to save all designations
SP:
ALTER PROCEDURE [dbo].[usp_GetDevices]
#id INT,
#designation NVARCHAR (MAX)
AS
BEGIN
declare #idsplat varchar(MAX)
set #idsplat = #UserIds
create table #u1 (id1 varchar(MAX))
set #idsplat = 'insert #u1 select ' + replace(#idsplat, ',', ' union select ')
exec(#idsplat)
Select
id FROM dbo.DevicesList WHERE id=#id AND designation IN (select id1 from #u1)
END
You need to use the boolean operators AND and OR in conjunction with LIKE:
IF empid = 1 AND (empname LIKE '%venkat%' OR empname LIKE '%vasu%')
The above example will return all rows with empid equals 1 and empname containing venkat or vasu.
Apparently you need to create that query based on the input from user, this is just an example of how the finally query should look like.
Edit: Trying to do this within SqlServer can be quite hard so you should really change your approach on how you call the stored procedure. If you can't do this then you could try and split your designation parameter on , (the answers to this question show several ways of how to do this) and insert the values into a temporary table. Then you can JOIN on this temporary table with LIKE as described in this article.

Connect By Prior Equivalent for MySQL

All,
I have three fields in a table that define a parent child relationship present in a MySQL database version 5.0 . The table name is tb_Tree and it has the following data:
Table Name: tb_Tree
Id | ParentId | Name
--------------------
1 | 0 | Fruits
2 | 0 | Vegetables
3 | 1 | Apple
4 | 1 | Orange
5 | 2 | Cabbage
6 | 2 | Eggplant
How do I write a Query to get all the children if a ParentId is specified. Note that the table entries given are just sample data and they can have many more rows. Oracle has a "CONNECT BY PRIOR" clause, but I didn't find anything similar for MySQL. Can anyone please advise?
Thanks
MySQL doesn't support recursive queries so you have to do it the hard way:
Select the rows where ParentID = X where X is your root.
Collect the Id values from (1).
Repeat (1) for each Id from (2).
Keep recursing by hand until you find all the leaf nodes.
If you know a maximum depth then you can join your table to itself (using LEFT OUTER JOINs) out to the maximum possible depth and then clean up the NULLs.
You could also change your tree representation to nested sets.
Might be late post.
With MySQL8 you can achieve it with recursive clause. Here is the example.
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
For more help find another thread, Hope It will help someone.
You Can also look into this interesting blog, which demonstrate how can we get similar results in mysql
http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
This is an old thread, but since I got the question in another forum I thought I'd add it here. For this case, I created a stored procedure that is hard-coded to handle the specific case. This do, of course have some drawbacks since not all users can create stored procedures at will, but nevertheless.
Consider the following table with nodes and children:
CREATE TABLE nodes (
parent INT,
child INT
);
INSERT INTO nodes VALUES
( 5, 2), ( 5, 3),
(18, 11), (18, 7),
(17, 9), (17, 8),
(26, 13), (26, 1), (26,12),
(15, 10), (15, 5),
(38, 15), (38, 17), (38, 6),
(NULL, 38), (NULL, 26), (NULL, 18);
With this table, the following stored procedure will compute a result set consisting of all the decedents of the node provided:
delimiter $$
CREATE PROCEDURE find_parts(seed INT)
BEGIN
-- Temporary storage
DROP TABLE IF EXISTS _result;
CREATE TEMPORARY TABLE _result (node INT PRIMARY KEY);
-- Seeding
INSERT INTO _result VALUES (seed);
-- Iteration
DROP TABLE IF EXISTS _tmp;
CREATE TEMPORARY TABLE _tmp LIKE _result;
REPEAT
TRUNCATE TABLE _tmp;
INSERT INTO _tmp SELECT child AS node
FROM _result JOIN nodes ON node = parent;
INSERT IGNORE INTO _result SELECT node FROM _tmp;
UNTIL ROW_COUNT() = 0
END REPEAT;
DROP TABLE _tmp;
SELECT * FROM _result;
END $$
delimiter ;
The below select lists all plants and their parentid up to 4-level (and of course you can extend the level):
select id, name, parentid
,(select parentid from tb_tree where id=t.parentid) parentid2
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4
from tb_tree t
and then you can use this query to get the final result. for example, you can get all children of "Fruits" by the below sql:
select id ,name from (
select id, name, parentid
,(select parentid from tb_tree where id=t.parentid) parentid2
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4
from tb_tree t) tt
where ifnull(parentid4,0)=1 or ifnull(parentid3,0)=1 or ifnull(parentid2,0)=1 or ifnull(parentid,0)=1
The below stored procedure order a table that has rows with back reference to the previous one. Notice on the first step I copy rows into temp table - those rows match some condition. In my case those are rows that belong to the same linear (road that is used in GPS navigation). Business domain is not important. Just in my case I am sorting segments that belong to the same road
DROP PROCEDURE IF EXISTS orderLocations;
DELIMITER //
CREATE PROCEDURE orderLocations(_full_linear_code VARCHAR(11))
BEGIN
DECLARE _code VARCHAR(11);
DECLARE _id INT(4);
DECLARE _count INT(4);
DECLARE _pos INT(4);
DROP TEMPORARY TABLE IF EXISTS temp_sort;
CREATE TEMPORARY TABLE temp_sort (
id INT(4) PRIMARY KEY,
pos INT(4),
code VARCHAR(11),
prev_code VARCHAR(11)
);
-- copy all records to sort into temp table - this way sorting would go all in memory
INSERT INTO temp_sort SELECT
id, -- this is primary key of original table
NULL, -- this is position that still to be calculated
full_tmc_code, -- this is a column that references sorted by
negative_offset -- this is a reference to the previous record (will be blank for the first)
FROM tmc_file_location
WHERE linear_full_tmc_code = _full_linear_code;
-- this is how many records we have to sort / update position
SELECT count(*)
FROM temp_sort
INTO _count;
-- first position index
SET _pos = 1;
-- pick first record that has no prior record
SELECT
code,
id
FROM temp_sort l
WHERE prev_code IS NULL
INTO _code, _id;
-- update position of the first record
UPDATE temp_sort
SET pos = _pos
WHERE id = _id;
-- all other go by chain link
WHILE (_pos < _count) DO
SET _pos = _pos +1;
SELECT
code,
id
FROM temp_sort
WHERE prev_code = _code
INTO _code, _id;
UPDATE temp_sort
SET pos = _pos
WHERE id = _id;
END WHILE;
-- join two tables and return position along with all other fields
SELECT
t.pos,
l.*
FROM tmc_file_location l, temp_sort t
WHERE t.id = l.id
ORDER BY t.pos;
END;