I have a MySQL table users:
# | name | parent|
________________________
1 | USER 1 |
2 | USER 2 |
3 | user 12 | 1
4 | user 22 | 2
5 | user 11 | 1
6 | USER 3 |
7 | user 21 | 2
8 | user 31 | 6
Here the parent record is the primary key of the same table. What i need is to sort the table both parent-wise and name-wise.
This is the result that I need to get:
# | name | parent|
________________________
1 | USER 1 |
5 | user 11 | 1
3 | user 12 | 1
2 | USER 2 |
7 | user 21 | 2
4 | user 22 | 2
6 | USER 3 |
8 | user 31 | 6
If you only have one level of parents you can do:
select u.*
from users u
order by coalesce(parent, #), #;
(This assumes that the id of the parent is smaller than the id of the children, as is the case with the sample data in the question.)
If you have multiple levels of parents (grandparents and so on), then a single MySQL query is problematic. MySQL doesn't directly support hierarchical or recursive queries.
Although I do not know your use case, I think what you are doing is a symptom of poor design.
If possible, a rethink might be in order.
Nevertheless, I think this works
SELECT * FROM users
ORDER BY
CASE `parent`
WHEN NOT NULL THEN `parent`
ELSE SUBSTRING_INDEX(`users`.`name`, ' ', -1) END
ASC
Simple explanation:
Sorts by parent field if not null.
Otherwise use the number found in the name field (extracted by separating by the space).
Assumes number will always be separated by a space, and as with Gordon's answer, only works with a single parent.
Related
This question already has answers here:
Custom SERIAL / autoincrement per group of values
(3 answers)
MySQL: Add sequence column based on another field
(3 answers)
MySQL auto-increment based on group
(3 answers)
Closed 4 years ago.
Teacher wants us to discuss how to implement the 'line_item' table of a receipt database for a global business.
He wants us to discuss the primary (composite?) key of such a 'line item' table. If our global business is printing thousands of receipts (with hundreds of items) every minute, how much do we need to consider the possibility of hitting the upper limits of an INT for the line_item_id? Is there a more effective way to handle the line_item id?
"Note: Gaps in numbering (generally caused by deletions) are okay. Also look for possible race conditions."
Consider this: You add 3 receipts with 3 items each. You delete the 2nd item of the 3rd receipt. Then add a new item to the 3rd receipt.
So I'm picturing something like this to start with: (Where receipt #1 and #3 were originally identical before the add/delete exercise. Note the numbering on #3.)
| receipt_id (FK) | line_id (INT) | line_text (TINYTEXT) |
---------------------------------------------------------------------
| 1 | 1 | 'Apple' |
| 1 | 2 | 'Banana' |
| 1 | 3 | 'Coconut' |
---------------------------------------------------------------------
| 2 | 1 | 'Apple' |
| 2 | 2 | 'Apple' |
| 2 | 3 | 'Apple' |
---------------------------------------------------------------------
| 3 | 1 | 'Apple' |
| 3 | 3 | 'Coconut' |
| 3 | 4 | 'Dates' |
---------------------------------------------------------------------
We've been learning SQL and PHP (mySQLi in innoDB if that matters much.)
However I feel this may be accomplished in SQL alone, right?
It appears I should use the MAX() function in the second column to make a COMPOSITE PRIMARY KEY and to avoid worrying about hitting the upper INT threshold.
I'm trying to concoct a MySQL INSERT that would do something dynamic with its numbering of the receipt line, like:
INSERT INTO line_item (3, VALUES MAX(receipt_line)+1, 'Dates')
At the end he has other thoughts like: "What if you now delete and add an item at the end of the second receipt?"
I imagine these are the operations he's looking for:
-- Delete the last line of the second receipt
DELETE FROM line_item WHERE receipt_id=2 AND line_id=MAX(line_id)
-- Add a new line to the second receipt.
INSERT INTO line_item VALUES ( 2, MAX(line_id)+1, 'Watermelon')
And I expect results like:
| receipt_id (FK) | line_id (INT) | line_text (TINYTEXT) |
---------------------------------------------------------------------
| 1 | 1 | 'Apple' |
| 1 | 2 | 'Banana' |
| 1 | 3 | 'Coconut' |
---------------------------------------------------------------------
| 2 | 1 | 'Apple' |
| 2 | 2 | 'Apple' |
| 2 | 3 | 'Watermelon' |
---------------------------------------------------------------------
| 3 | 1 | 'Apple' |
| 3 | 3 | 'Coconut' |
| 3 | 4 | 'Dates' |
---------------------------------------------------------------------
But instead I get a bunch of errors and weird results.
Do I need to use PHP to calculate the line_id instead? What am I missing?
You can't use just MAX(line_id) because that gets the maximum value of line_id across all receipts. What you want is the maximum line_id in one specific receipt. Something like SELECT MAX(line_id) FROM line_item WHERE receipt_id = 3.
You could build that into a single SQL query using a sub select to get the new line_id value. But I think it would be better done in PHP. Determine the new line_id first, then insert your new line item. In two separate queries.
Also think about how your receipt application will delete a line item. If I want to delete the second Apple line item in receipt 2 I would need to know it's line_id. So your application will need to know the line_id of new items it adds.
I have the following simplified table: (Note we skipped 2nd exam in the exam_id)
+----+---------+-------+
| id | exam_id | score |
+----+---------+-------+
| 1 | 1 | 15 |
| 2 | 1 | 20 |
| 3 | 1 | 68 |
| 4 | 3 | 92 |
| 5 | 3 | 10 |
+----+---------+-------+
I want to write an $sql and some php (I'm using Wordpress, and can use $wpdb) to be able to get the following:
$exam[3]=10
$exam[1]=68
Not that when there are multiple exams, we take the score entry which corresponds to the largest id
And $exam[2] is empty. In words, I'd like to save the last ever exam that the user attempted and show their score.
I've tried using Group By and
Try this
SELECT
*
FROM exams
WHERE exams.id IN (
SELECT
MAX(id)
FROM exams
GROUP BY exam_id
);
i'm trying to get all parents of children. I have a simple query
select from sections where sectionID = 6
The database structure is very simple
sectionID parent
1 0
2 1
3 1
4 2
5 4
6 5
Now I'm trying to get all parents from sectionID 6, the result should be a string 0/2/4/5/6.
children might not have a lot of parents, it might have only one so the result should be different. i.e
5/6.. I hope you understand what I'm trying to do. I have tried couple recursive functions which i've found on the internet, but I really suck at this and was wondering if anyone could help me to get on the right track. P.S I'm using php and mysql
Well, as far as I can see you have two choices, both well known.
1) You make a recursive function, just the ones you have been trying. There are tons of them around and i won't put him in here.
2) By far my favourite, this is the Database pattern most modern ORM use, it's called nested set model.
Basically you create a few more columns on the table, it should look like this one:
CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
INSERT INTO nested_category VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);
SELECT * FROM nested_category ORDER BY category_id;
+-------------+----------------------+-----+-----+
| category_id | name | lft | rgt |
+-------------+----------------------+-----+-----+
| 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 |
| 3 | TUBE | 3 | 4 |
| 4 | LCD | 5 | 6 |
| 5 | PLASMA | 7 | 8 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 7 | MP3 PLAYERS | 11 | 14 |
| 8 | FLASH | 12 | 13 |
| 9 | CD PLAYERS | 15 | 16 |
| 10 | 2 WAY RADIOS | 17 | 18 |
+-------------+----------------------+-----+-----+
If you notice there is no parent_id column. To be able to search for it's childrens for, let's say row 5 the query will be like:
Select * from nested_category where left > 7 and left < 8 order by left asc, which will bring no results.
For row number 1 the result however will bring the entire tree.
I have no php script for autocreating those columns on this computer, but there are plenty around. Im afraid they are also recursive.
You my find lots of info around searching for "nested set model", like this or theorical exaplanations of the model like this one
AND THIS IS A WAY DUPLICATED QUESTION (i can not put it as duplicated)
Some other answers:
This guys do it all in mysql
Some more answers here
You should reread the forum rules before posting, look for already asked questions.
Hope it helps.
I'm not going to write all the code, but this idea should work if I understand your database shema correctly.
section = 6
result = ""
while section != ""
select parent where sectionID = section
result += parent
section = parent
return result
I need to show 1000 test questions to a student, 10 per page.
The questions are in a mysql table, the answers will go in another table.
I need each students questions to appear in a different predetermined order than any other students. The sort order is predetermined when they register and placed in a usermeta table.
In the usermeta table there is a column that lists the order in which the questions should be shown. The order in that column is unique to each student and looks like this example: 8|14|97|54|21|37|54|61 ...etc.
The first question to be shown to the student would be question #8, and then question #14, and then question #97, and so on, listing 10 per page.
I don't need to sort the questions asc or desc. Also, I can change the db structure if that would help find a solution.
Also, I can change the db structure if that would help find a
solution.
If changing the db structure is possible, then instead of storing the sorting order as a pipe separated string, store it in a separate table that maps each question to the order it should appear in for a given student. i.e.
student_id, sort_order, question_id
1 1 8
1 2 2
1 3 97
Then join on your sorting table when selecting your questions for a particular student.
SELECT q.* FROM
questions q
JOIN questions_sorting_order qso
ON q.id = qso.question_id
ORDER BY qso.sort_order
WHERE qso.student_id = :student_id
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i2.i*10+i1.i x
, FIND_IN_SET(i2.i*10+i1.i,'8,14,97,54,21,37,54,61') y
FROM ints i1
, ints i2
HAVING y > 0
ORDER
BY y;
+----+---+
| x | y |
+----+---+
| 8 | 1 |
| 14 | 2 |
| 97 | 3 |
| 54 | 4 |
| 21 | 5 |
| 37 | 6 |
| 61 | 8 |
+----+---+
Note that 54 is ignored second time around
I have the following table structure
+-------+------------+-----------+---------------+
| id |assigned_to | status | group_id |
+-------+------------+-----------+---------------+
| 1 | 1001 | 1 | 19 |
+-------+------------+-----------+---------------+
| 2 | 1001 | 2 | 19 |
+-------+------------+-----------+---------------+
| 3 | 1001 | 1 | 18 |
+-------+------------+-----------+---------------+
| 4 | 1002 | 2 | 19 |
+-------+------------+-----------+---------------+
| 5 | 1002 | 2 | 19 |
+-------+------------+-----------+---------------+
I would like to get the information in the following format
+-------+------------+-----------+
| | 1001 | 1002 |
+-------+------------+-----------+
| 1 | 1 | 0 |
+-------+------------+-----------+
| 2 | 1 | 2 |
+-------+------------+-----------+
So basically I am looking to use the assigned to field as the column names. Then the rows represent the status. So for example in the table we have two rows where user 1002 has a status of 2, therefore the sum is shown on that particular status row.
Please note that the group_id must be 19. Hence why I left out the row with id 3 on my table.
Can someone point me in the right direction. Im sure there is a name for this type of query, but I can't for the life of me put this into words. I have tried various other queries, but none of them even come close to this.
Marc B is right, there is no way to pivot a table -i.e. converting the content of a field into columns- unless you make some assumptions, like supossing that the values of assigned_to are somewhat fixed.
On the other hand, this is the kind of problems that can be solved by a program. It is not an easy program, but it can do the job.
I recently made a program similar to this in java, if you are interested I can post the core of it here.
you might want to read this article http://www.artfulsoftware.com/infotree/qrytip.php?id=523
i'd be something like
SELECT
assigned_to,
COUNT( CASE assigned_to WHEN '1001' THEN 1 ELSE 0 END ) AS '1001',
COUNT( CASE assigned_to WHEN '1002' THEN 1 ELSE 0 END ) AS '1002'
FROM table
WHERE group_by = 19
GROUP BY assigned_to WITH ROLLUP;
or something like that (i haven't tested this code.. )
in the article, he does it using SUM() you'd have to do it with COUNT() and add a WHERE constraint for the group_id
Hope this helps