Update A star column till root node on adding new entry - mysql

I don't know how to solve this case need some guidance I have a scenario where I have to update a star column field of root node on adding every child entry so far I have done this, its updating the star column of directly linked nodes not the full path like if A is root and B C & D are children then A star column gets update now if C B or D adds something below them A stars column doesn't gets updated, I have less knowledge about triggers and sql and I am stuck I have searched a lot but didn't find a solution here is the method of sql which is doing all this so far.
DELIMITER ##
DROP PROCEDURE p_prefix_nodes_add_new_paths_after_insert ##
CREATE PROCEDURE cvs.p_prefix_nodes_add_new_paths_after_insert
(
param_node_new_id INT UNSIGNED,
param_node_parent_id INT UNSIGNED
)
BEGIN
INSERT INTO `prefix_nodes_paths` (
`ancestor_id`,
`descendant_id`,
`path_length`
)
SELECT
`ancestor_id`,
`param_node_new_id`,
`path_length` + 1
FROM
`prefix_nodes_paths`
WHERE `descendant_id` = `param_node_parent_id`
UNION
ALL
SELECT
`param_node_new_id`,
`param_node_new_id`,
0 ;
Update prefix_nodes_paths
Set stars=stars+1
where ancestor_id=param_node_parent_id;
END ##
DELIMITER ;

Id's suggest you to use the nested set model technique to store your tree-like data in the SQL database. Your queries would become a lot more handy, faster and simpler when you try to get descendants or ancestors of the specific node.

Related

sql server trigger to mysql

I'm trying to migrate an SQL Server 2012 trigger to MySQL. The trigger helps to create a hierarchical path for a tree of parent-child nodes which looks something like ".level1.level2.level3" and so on. However, I'm having some issues getting it to work. Here is the SQL Sever trigger creation:
CREATE TRIGGER [dbo].[trigger_TiersPath] ON [dbo].[Tiers] AFTER INSERT, UPDATE
AS
BEGIN
-- This trigger will update the hierarchy diagram for the inserted/updated node and all descendants.
IF (UPDATE(ParentID)) -- Ignore updates to other data fields
BEGIN
-- First, all updated nodes will start with the same string: the new hierarchy diagram of the
-- updated/inserted node. Find this first, then use it as the base for all updated values.
DECLARE #newDiagramStart VARCHAR(MAX)
-- Just use a dot if this is the top of the hierarchy. Otherwise, obtain the hierarchy diagram of the new parent.
SELECT #newDiagramStart = ISNULL(Tiers.TierPath, '.') FROM inserted LEFT JOIN Tiers ON Tiers.TierID = inserted.ParentID
-- Append the inserted/updated node ID.
SELECT #newDiagramStart = #newDiagramStart + CAST(TierID AS VARCHAR(MAX)) + '.' FROM inserted
-- Now update the hierarchy diagram for the inserted/updated node and any descendants.
UPDATE NodesDescendants
-- Find the existing hierarchy diagram after the node ID of the inserted/updated node, and append it.
SET TierPath = #newDiagramStart + ISNULL(RIGHT(NodesDescendants.TierPath, LEN(NodesDescendants.TierPath) - LEN(inserted.TierPath)),'')
FROM inserted
INNER JOIN Tiers AS NodesDescendants
ON NodesDescendants.TierID = inserted.TierID -- Update the inserted/updated node.
OR NodesDescendants.TierPath LIKE inserted.TierPath + '%' -- Update all descendants.
END
END
;
So far, this is what I have for the Insert portion of the equivalent MySQL trigger:
DELIMITER //
CREATE TRIGGER trigger_TiersPath
BEFORE INSERT
ON Tiers FOR EACH ROW
BEGIN
DECLARE newDiagramStart LONGTEXT;
CREATE TEMPORARY TABLE IF NOT EXISTS InsTiers
(
TierID VARCHAR(50),
ParentID VARCHAR(50),
OrgID VARCHAR(50),
TierPath VARCHAR(2048),
Active CHAR(1),
CreatedON DATETIME,
CreatedBY VARCHAR(50),
BatchID INT
);
DELETE FROM InsTiers;
INSERT INTO InsTiers VALUES(NEW.TierID, NEW.ParentID, NEW.OrgID, NEW.TierPath, NEW.Active, NEW.CreatedON, NEW.CreatedBY, NEW.BatchID);
BEGIN
-- This trigger will update the hierarchy diagram for the inserted/updated node and all descendants.
IF (1 = 1) then -- Ignore updates to other data fields
-- First, all updated nodes will start with the same string: the new hierarchy diagram of the
-- updated/inserted node. Find this first, then use it as the base for all updated values.
-- Just use a dot if this is the top of the hierarchy. Otherwise, obtain the hierarchy diagram of the new parent.
select IFNULL(Tiers.TierPath,'.') INTO newDiagramStart FROM InsTiers inserted LEFT JOIN Tiers ON Tiers.TierID = inserted.ParentID;
-- Append the inserted/updated node ID.
-- Find the existing hierarchy diagram after the node ID of the inserted/updated node, and append it.
-- Update the inserted/updated node.
SET newDiagramStart = CONCAT_WS('', newDiagramStart, CAST(NEW.TierID AS CHAR), '.');
-- Now update the hierarchy diagram for the inserted/updated node and any descendants.
NEW.TierPath = CONCAT_WS('',newDiagramStart,IFNULL(RIGHT(NULL, CHAR_LENGTH(NULL) - CHAR_LENGTH(NEW.TierPath)),''));
end if;
END;
END;
//
However, this fails due to some syntax error related to both of the CONCAT_WS lines. I just can't see it. I'm open to suggestions to fix this; or even, finding a better approach to achieve the same ends. Unfortunately, I'm stuck with keeping the whole ".level1.level2.level3" type of concatenated path. In my case those "levels" are actually IDs.
Thanks ahead of time.

MySQL temporary tables do not clear

Background - I have a DB created from a single large flat file. Instead of creating a single large table with 106 columns. I created a "columns" table which stores the column names and the id of the table that holds that data, plus 106 other tables to store the data for each column. Since not all the records have data in all columns, I thought this might be a more efficient way to load the data (maybe a bad idea).
The difficulty with this was rebuilding a single record from this structure. To facilitate this I created the following procedure:
DROP PROCEDURE IF EXISTS `col_val`;
delimiter $$
CREATE PROCEDURE `col_val`(IN id INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp_record;
CREATE TEMPORARY TABLE tmp_record (id INT(11), val varchar(100)) ENGINE=MEMORY;
SET #ctr = 1;
SET #valsql = '';
WHILE (#ctr < 107) DO
SET #valsql = CONCAT('INSERT INTO tmp_record SELECT ',#ctr,', value FROM col',#ctr,' WHERE recordID = ',#id,';');
PREPARE s1 FROM #valsql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SET #ctr = #ctr+1;
END WHILE;
END$$
DELIMITER ;
Then I use the following SQL where the stored procedure parameter is the id of the record I want.
CALL col_val(10);
SELECT c.`name`, t.`val`
FROM `columns` c INNER JOIN tmp_record t ON c.ID = t.id
Problem - The first time I run this it works great. However, each subsequent run returns the exact same record even though the parameter is changed. How does this persist even when the stored procedure should be dropping and re-creating the temp table?
I might be re-thinking the whole design and going back to a single table, but the problem illustrates something I would like to understand.
Unsure if it matters but I'm running MySQL 5.6 (64 bit) on Windows 7 and executing the SQL via MySQL Workbench v5.2.47 CE.
Thanks,
In MySQL stored procedures, don't put an # symbol in front of local variables (input parameters or locally declared variables). The #id you used refers to a user variable, which is kind of like a global variable for the session you're invoking the procedure from.
In other words, #id is a different variable from id.
That's the explanation of the immediate problem you're having. However, I would not design the tables as you have done.
Since not all the records have data in all columns, I thought this might be a more efficient way to load the data
I recommend using a conventional single table, and use NULL to signify missing data.

Mysql increment in loop by variable

I am trying to use a simple MySQL query to update my table with positions.
Let's assume I have a book table with an ID, a writer_id and a position field.
I want to be able to have positions from 1 ... x , but per writer_id.
If I delete a record there will be a gap in my positions so this is why I want a simple query to reset all the positions without gaps.
Currently I have the following code (which works), but I think this should be possible a lot easier (and probably faster).
set #position := 0;
set #lastDependency := 0;
set #previousDependency := -1;
UPDATE `book` SET
`writer_id`=(#lastDependency:=`writer_id`), -- Set writer_id of current row
position=(
IF (
NOT #lastDependency=#previousDependency,
#position:=1, -- New writer_id => set position to 1
#position:=#position+1 -- Same writer id, increment position
)
),
`writer_id`=(#previousDependency:=`writer_id`) -- Set writer_id of last used row
ORDER BY `writer_id`, position ASC -- Order by current positions
I can also use PHP to loop through all my records and save them one by one, but I guess that won't be any better
why don't you use a trigger with the following function:
"when a row is deletet, reduce evere writer_id that is greater than the deleted ones by one"
or to say it in pseudo-code:
create trigger for delete...
update book
set writer_id = writer_id - 1
where writer_id > deleted.writer_id
Let me quote the MySQL documentation to you:
As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed. The order of evaluation for expressions involving user variables is undefined and may change based on the elements contained within a given statement; in addition, this order is not guaranteed to be the same between releases of the MySQL Server.
So the way you attempt to do things may work, but comes with absolutely no guarantees. Therefore I'd suggest you do this in PHP instead of MySQL. Or you do it in MySQL using a stored procedure.

Call a Stored Procedure From a Stored Procedure and/or using COUNT

Ok, First off, I am not a mysql guru. Second, I did search, but saw nothing relevant related to mysql, and since my DB knowledge is limited, guessing syntactical differences between two different Database types just isn't in the cards.
I am trying to determine if a particular value already exists in a table before inserting a row. I've decided to go about this using two Stored procedures. The first:
CREATE PROCEDURE `nExists` ( n VARCHAR(255) ) BEGIN
SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) as T;
END
And The Second:
CREATE PROCEDURE `createUser` ( n VARCHAR(255) ) BEGIN
IF (nExists(n) = 0) THEN
INSERT INTO Users...
END IF;
END
So, as you can see, I'm attempting to call nExists from createUser. I get the error that no Function exists with the name nExists...because it's a stored procedure. I'm not clear on what the difference is, or why such a difference would be necessary, but I'm a Java dev, so maybe I'm missing some grand DB-related concept here.
Could you guys help me out by any chance?
Thanks
I'm not sure how it helped you, but...
why SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) and not just SELECT COUNT(*) FROM Users WHERE username=n?
Just make the user name (or whatever the primary application index is) a UNIQUE index and then there is no need to test: Just try to insert a new record. If it already exists, handle the error. If it succeeds, all is well.
It can (and should) all be one stored procedure.

How to call a stored procedure and alter the database table in 1 go

I'm really struggeling with this for some time now.
I have a MySQL database and a lot of data. It is a formula1 website i have to create for college.
Right now the j_tracks_rounds_results table is filled with data but one column is not filled out. It's the rank column.
I created a stored procedure as the following:
DROP PROCEDURE `sp_rank`//
delimiter ##
PROCEDURE `sp_rank`(sid INT)
begin
set #i = 0;
SELECT `performance`, subround_id, #i:=#i+1 as rank
FROM `j_tracks_rounds_results`
where subround_id = sid
order by `subround_id`,`performance`;
end
delimiter ;
The output is like the following:
rec.ID PERFORMANCE SUBROUND_ID RANK
87766 100,349114516829 1 1
93040 101,075635087628 1 2
88851 101,664302543497 1 3
It gets the results and ads a rank to it, sorted on performance so the lowest performance gets rank1 etc...
What i am trying to achieve is to put the rank back into the table. Like an ALTER command for the column "rank".
How would i be able to accomplish this?
Basically don't...
Create table to hold the key (rec.id ?) and the rank. Truncate it to get rid of the previous results then use insert into ... with your query and then join to it.
You really don't want to be altering tables in your normal running, guaranteed some one will use the column when it isn't there, and then when you look at the fault it will be...
People just don't look for table structures changing through the application lifetime, it's a screw up waiting to happen.
You are misapplying your SQL statements. You want the UPDATE command, not ALTER.
eg.
UPDATE table SET rank=#i WHERE subround_id=#id