Sort table records in special order - mysql

I have table:
+----+--------+----------+
| id | doc_id | next_req |
+----+--------+----------+
| 1 | 1 | 4 |
| 2 | 1 | 3 |
| 3 | 1 | 0 |
| 4 | 1 | 2 |
+----+--------+----------+
id - auto incerement primary key.
nex_req - represent an order of records. (next_req = id of record)
How can I build a SQL query get records in this order:
+----+--------+----------+
| id | doc_id | next_req |
+----+--------+----------+
| 1 | 1 | 4 |
| 4 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 0 |
+----+--------+----------+
Explains:
record1 with id=1 and next_req=4 means: next must be record4 with id=4 and next_req=2
record4 with id=5 and next_req=2 means: next must be record2 with id=2 and next_req=3
record2 with id=2 and next_req=3 means: next must be record3 with id=1 and next_req=0
record3 with id=3 and next_req=0: means that this is a last record
I need to store an order of records in table. It's important fo me.

If you can, change your table format. Rather than naming the next record, mark the records in order so you can use a natural SQL sort:
+----+--------+------+
| id | doc_id | sort |
+----+--------+------+
| 1 | 1 | 1 |
| 4 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
+----+--------+------+
Then you can even cluster-index on doc_id,sort for if you need to for performance issues. And honestly, if you need to re-order rows, it is not any more work than a linked-list like you were working with.

Am able to give you a solution in Oracle,
select id,doc_id,next_req from table2
start with id =
(select id from table2 where rowid=(select min(rowid) from table2))
connect by prior next_req=id
fiddle_demo

I'd suggest to modify your table and add another column OrderNumber, so eventually it would be easy to order by this column.
Though there may be problems with this approach:
1) You have existing table and need to set OrderNumber column values. I guess this part is easy. You can simply set initial zero values and add a CURSOR for example moving through your records and incrementing your order number value.
2) When new row appears in your table, you have to modify your OrderNumber, but here it depends on your particular situation. If you only need to add items to the end of the list then you can set your new value as MAX + 1. In another situation you may try writing TRIGGER on inserting new items and calling similar steps to point 1). This may cause very bad hit on performance, so you have to carefully investigate your architecture and maybe modify this unusual construction.

Related

MySql add relationships without creating dupes

I created a table (t_subject) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
And another table (t_place) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | d | 1 |
| 2 | e | 1 |
| 3 | f | 1 |
Right now data from t_subject is used for each of t_place records, to show HTML dropdowns, with all the results from t_subject.
So I simply do
SELECT * FROM t_subject WHERE enabled = 1
Now just for one of t_place records, one record from t_subject should be hidden.
I don't want to simply delete it with javascript, since I want to be able to customize all of the dropdowns if anything changes.
So the first thing I though was to add a place_id column to t_subject.
But this means I have to duplicate all of t_subject records, I would have 3 of each, except one that would have 2.
Is there any way to avoid this??
I thought adding an id_exclusion column to t_subject so I could duplicate records only whenever a record is excluded from another id from t_place.
How bad would that be?? This way I would have no duplicates, so far.
Hope all of this makes sense.
While you only need to exclude one course, I would still recommend setting up a full 'place-course' association. You essentially have a many-to-many relationship, despite not explicitly linking your tables.
I would recommend an additional 'bridging' or 'associative entity' table to represent which courses are offered at which places. This new table would have two columns - one foreign key for the ID of t_subject, and one for the ID of t_place.
For example (t_place_course):
| place_id | course_id |
|----------|-----------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 3 |
As you can see in my example above, place 3 doesn't offer course 2.
From here, you can simply query all of the courses available for a place by querying the place_id:
SELECT * from t_place_course WHERE place_id = 3
The above will return both courses 1 and 3.
You can optionally use a JOIN to get the other information about the course or place, such as the description:
SELECT `t_course`.`description`
FROM `t_course`
INNER JOIN `t_place_course`
ON `t_course`.`id` = `t_place_course`.`course_id`
INNER JOIN `t_place`
ON `t_place`.`id` = `place_id`

How to update rows of MySQL without stored function?

My cloud server doesn't allow stored function/procedure, so how to do this?
My table was:
|id |Status |
| 1 | Good |
| 2 | Badd |
| 3 | Good |
| 4 | Good |
I wanted it to be:
| 1 | Good |
| 3 | Badd |
| 4 | Good |
| 5 | Good |
Can this be done with one line of statement? Basically I want to change all id in rows where id>x to id+1. In this case, x = 2.
Update
Forgot to mention that id is unique so how to sort them desc then change?
You can use
UPDATE yourtable SET id = id + 1 WHERE id > 1 ORDER BY ID DESC;
there is a known "bug" or "feature" that you will face if id is unique but order by desc will help you to avoid it.
Try this
UPDATE `table` SET id = id + 1 WHERE id > 1

MySQL only one active record, multiple inactive records

I've got this MySQL-table:
id | staffId | companyId | active | somevalue
When I create an UNIQUE index on staffId, companyId and active I can store a maximum of two records. One with active 1 and one with active 0.
I would like to make sure I can have only one active record while it is possible to have multiple inactive records. For example:
1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 0 | 5
1 | 1 | 1 | 0 | 7
Should be possible, but adding this record should cause an error:
1 | 1 | 1 | 1 | 9
Choosing NULL instead of 0 for an incactive record seems to work :-) I wonder if there are better solutions?

SQL 'COUNT' not returning what I expect, and somehow limiting results to one row

Some background: an 'image' is part of one 'photoshoot', and may be a part of zero or many 'galleries'. My tables:
'shoots' table:
+----+--------------+
| id | name |
+----+--------------+
| 1 | Test shoot |
| 2 | Another test |
| 3 | Final test |
+----+--------------+
'images' table:
+----+-------------------+------------------+
| id | original_filename | storage_location |
+----+-------------------+------------------+
| 1 | test.jpg | store/test.jpg |
| 2 | test.jpg | store/test.jpg |
| 3 | test.jpg | store/test.jpg |
+----+-------------------+------------------+
'shoot_images' table:
+----------+----------+
| shoot_id | image_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
| 3 | 3 |
+----------+----------+
'gallery_images' table:
+------------+----------+
| gallery_id | image_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 1 |
+------------+----------+
What I'd like to get back, so I can say 'For this photoshoot, there are X images in total, and these images are featured in Y galleries:
+----+--------------+-------------+---------------+
| id | name | image_count | gallery_count |
+----+--------------+-------------+---------------+
| 3 | Final test | 1 | 1 |
| 2 | Another test | 0 | 0 |
| 1 | Test shoot | 2 | 4 |
+----+--------------+-------------+---------------+
I'm currently trying the SQL below, which appears to work correctly but only ever returns one row. I can't work out why this is happening. Curiously, the below also returns a row even when 'shoots' is empty.
SELECT shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
ORDER BY shoots.id DESC
Thanks for taking the time to look at this :)
You are missing the GROUP BY clause:
SELECT
shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
GROUP BY 1, 2 -- Added this line
ORDER BY shoots.id DESC
Note: The SQL standard allows GROUP BY to be given either column names or column numbers, so GROUP BY 1, 2 is equivalent to GROUP BY shoots.id, shoots.name in this case. There are many who consider this "bad coding practice" and advocate always using the column names, but I find it makes the code a lot more readable and maintainable and I've been writing SQL since before many users on this site were born, and it's never cause me a problem using this syntax.
FYI, the reason you were getting one row before, and not getting and error, is that in mysql, unlike any other database I know, you are allowed to omit the group by clause when using aggregating functions. In such cases, instead of throwing a syntax exception, mysql returns the first row for each unique combination of non-aggregate columns.
Although at first this may seem abhorrent to SQL purists, it can be incredibly handy!
You should look into the MySQL function group by.

How to update a 'Sort Index' column of a list of records with one call

Basically, I have a list of records in a mysql db. These records are ordered 1 to 10. The user can re-order these records to whatever order they want. They will press a button to update all the records to their newly, respective order number. For example:
ID | Sort_Index | Name
----------------------
1 | 1 | Jim
2 | 2 | Bob
3 | 3 | Carl
4 | 4 | Bill
5 | 5 | Wendy
The user can change these to this for example:
Note: the changed values are stored into an array before I make the UPDATE calls
ID | Sort_Index | Name
----------------------
1 | 1 | Carl
2 | 2 | Wendy
3 | 3 | Bob
4 | 4 | Jim
5 | 5 | Bill
My question is, how can I make this mysql call with one call, using the new values in my array, instead of one call for each record?
If this is impossible or simply the "wrong way to do it", please feel free to suggest new ideas as I am not fully committed to this idea as of now.
If you have a limited number of rows, you could implement this with an sql CASE statement --
Update users set sort_index = case id when 1 then <newval> when 2 then <newval>...