MySQL - Getting the top-level parent id in a hierarchy - php

Here's my table structure...
TABLE : position_hierarchy_level
id parent_position_id position_id
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
6 6 7
7 7 8
8 8 9
9 9 10
10 10 11
11 11 12
12 12 13
13 13 14
14 14 15
My query for getting the parent_position_id of a certain position_id is:
select `parent_position_id` from `position_hierarchy_level` where position_id= 15;
But how can I get the top-most parent of a certain position_id? For example, the top-most parent_position_id of position_id 15 would be 1.
Is there a convenient way to get this value using a single query? Or do I need to create a loop in PHP?

Your database structure wouldnt let you do it unless there are 15 or more joins. You are using Adjacency list model. Try using The nested set model
Here is an example with php

Try this:
DELIMITER $$
CREATE FUNCTION getTopParentPositionId(positionId INT) RETURNS INT DETERMINISTIC
BEGIN
DECLARE x INT;
DECLARE y INT;
SET x = positionId;
sloop:LOOP
SET y = NULL;
SELECT parent_position_id INTO y FROM position_hierarchy_level WHERE position_id = x;
IF y IS NULL THEN
LEAVE sloop;
END IF;
SET x = y;
ITERATE sloop;
END LOOP;
RETURN x;
END $$
DELIMITER ;
Then:
SELECT getTopParentPositionId( 5 );
Obviously, you are not the only one who looked into thoose kind of solution :)

Looks like the same problem:
Recursive PHP function for adjacency-list display
Using a single query with mysql could be kind of different. Maybe you could solve this with a stored procedure.

if i understand correctly and you want to highest position_id to have 1 for parent_position_id and so on ...
1 . SET parent_position_id to auto increment
2 . select position_id from table order by position_id desc and put them in a array
3 . truncate table
4 . insert array to table

With this table structure the best option you have is a loop in php end of things.
If the table structure is something you can freely change (in case if project isn't live already), you might want to look into structure called Closure Tables. You can find a simple example of how the are used/setup in this article.
In any case, you should be able to find a lot more on the subject in the SQL Antipatterns book.

Is there a convenient way to get it using a single query?
I think NO, take look here for Hierarchical queries in MySQL
do I need to create a loop statement in PHP?
I think YES.

Related

Can 4 fields be put together to use as a key to join two tables?

I am trying to create a solution which allows pilots to swap a 1, 2, 3 or 4 day trip for another pilot's 1, 2, 3 or 4 day trip. I have 3 tables, Pilot, Have and Want. A pilot creates a Have which, for example, is a 2 day on 10/20 (October 20th). This pilot wants a 3 day starting on the 22. Another pilot has the 3 day and he wants something like the other pilot's 2 day. The tables looks like this;
id_pilot, name, phone, employee_num, aircraft, base, seat
1 Steve 363-0040 123454 320 DCA FO
2 Ted 992-5380 123455 320 DCA FO
id_have, id_pilot, daytrip, start_month, start_day
1 1 02 10 20
2 2 03 10 22
id_want, id_have, daytrip, start_month, start_day
1 1 03 10 22
To see what Wants are out there for a particular Have I need to join the Want and Have table on something that looks like DCA|320|FO|10|20|2. I only want to see the Wants for a particular Have that are for the aircraft, base and seat. I can do this by creating a new join field but having such a simplistic understanding of MySQL I imagine there is a way to do this on the fly. I used joins to grab information via the primary keys but this seems like it's one step removed from that. What would such a query look like?
To give an example of what Marc B is saying, the join clause can compare the daytrip, start_month and start_date columns between the have and want tables, e.g.
select have.id_pilot, want.id_pilot, want.daytrip, want.start_month, want.start_day
from have
inner join want
on have.daytrip = want.daytrip
and have.start_month = want.start_month
and have.start_day = want.start_day
The 'on' clause is executed on each row comparison between the two tables and the only absolute is that it must return true or false. So any column from either table can be used in the evaluation in any combination.

Lowest free value in mysql column

I already searched but I always find LEAST and GREATEST as hints. I want to have the next ascending number in a row that's not used. Like the following:
entries
1
2
3
5
6
7
If every of the numbers is for one row in my table I want the number 4 as a result and in the following example:
1
2
3
4
5
6
I want the number 7 as a result. Is there any possiblity to accomplish this in an SQL statement?
Best,
Robin
This query assumes that the number 1 is in your table
select min(number) + 1 from entries e1
where not exists (
select 1 from entries e2
where e2.number = e1.number + 1
)
If you want all missing numbers (where gaps are no larger than 1) instead of the smallest one, then remove min()
It think the solution is to do a self-join with the next value, and extract the first lowest result. Example:
Table: values, with column value
SELECT v1.value
FROM values v1
LEFT JOIN values v2 ON v1.value = (v2.value + 1)
WHERE v2.value IS NULL
ORDER BY v1.value ASC
LIMIT 1

Detecting columns in a document (table) via php - Algorithm

Given a table like the one below, what would be the best way to detect the two columns separately?
So what I would need the total colspans for the first column.
What is important to remember is that the nr of columns can change.
In the case of this example, the second column starts at "10 euro" (second row). The first section is equal to 2 colspans. The other section is 5 colspans.
Any (abstract) ideas on how to do this?
You must consider the gaps in between the table cells and mark their positions, like this::
0 1 2 3 4 7
0 2 3 4 5 6 7
0 1 2 4 5 7
...
0 2 7
Once you have built an array with above information, you iterate over them and mark the common gap locations:
0 2 7
Since 0 and 7 are both at the edges of your table, you can strip those off. Then you're left with position 2 as the common gap between your rows.
Done :)

MySQL - Updating and incrementing a column value, starting from 1, for a range of rows based on and sorted by another column

I have this table:
id track_name datetime weight
1 aName 2010-06-01 09:00:00 1
2 theName3 2010-07-01 11:00:00 2
3 heyThere 2010-08-01 16:00:00 3
4 abcd 2010-08-01 22:44:00 4
5 g123go 2010-08-01 22:00:50 5
6 foobar 2010-09-01 13:11:00 6
7 barfoo 2010-11-01 12:00:55 7
8 barbar 2010-12-01 11:11:00 8
The weight determines the row record order. It is used for ordering a playlist. And the user can move items up and down, thus reordering in the simple fashion, in which works great.
Now I wonder if there is possible to write a single query that can change the 'weight' value based on the 'date' column, ordering by either DESC or ASC. The same for the 'track_name' column.
Example pseudo query:
UPDATE table SET weight (start from 1) ORDER BY datetime ASC
My alternative is to fetch all rows and process each and everyone of them on the web server, which I doubt is the most effecient way, if there are thousands of records.
I don't think you can do it with a single query. You need a counter, this means you need a loop. If you want to do it with MySQL only, you can create a stored procedure. If not, just write a PHP script (witch might will be a bit slower). Logic is the same:
Get all the data from the table;
Loop through every record in the
order you need and update weight
parameter.
You could use a temporary table with an auto incremented id and select insert into it using your order.

SQL - Query Help: Finding a Local Maximum

I have a table which has data from a graph.
for example
index value
0 3
1 5
2 7
3 6
4 8
5 9
6 12
7 11
8 10
9 14
10 13
I need to a query that returns the results where the value is at a local maximum, i.e. the value at a particular index is greater than the value at index+1 and index-1.
So for the example set, it should return the list of indexes: 2, 6, 9 corresponding to values 7, 12, 14.
I'm using PHP with SQLite.
I can do it with a foreach-loop in php, but was wondering if there's an easy way to do it using just SQL commands.
Any input would be appreciated.
Or use sub-queries (this is tested):
select ind
from tmp1 t1
where val > (select val from jdoyle.tmp1 t2 where t2.ind = t1.ind-1)
and val > (select val from jdoyle.tmp1 t2 where t2.ind = t1.ind+1);
Doing this with a single loop in PHP is likely to be much faster than shoehorning this into an SQL query, but if you really want to you could self-join the table with itself with something like:
SELECT b.index
FROM points AS a, points AS b, points AS c
WHERE a.index = b.index-1 AND c.index = b.index+1
AND a.value < b.value AND c.value < b.value
(Untested, so *cross fingers*.)
You could write your loop inside a stored procedure in SQL if your SQL database supports stored procedures. However, I don't think sqlite has rich enough stored procedures to do something like this, you would need to use mysql, postgresql or similar.
Well, if you're using sqlite on production I imagine that you don't have a huge bunch of data. Considering this, the best solution really is to solve it at php level.

Categories