SQL: counting rows refering to tables own id - mysql

I have a table set up like this (simplified for this example)
ID | int |Primary auto increment
Name | text |
ContainerID | int |
ContainsCount | int |
I am using the container id to refer to the table itself. So if i have a row with
ID:3 Name:Test1 ContainerID:0 ContainsCount:0
ID:4 Name:Test2 ContainerID:3 ContainsCount:0
ID:5 Name:Test3 ContainerID:3 ContainsCount:0
It means that the row 4 and 5 is inside row 3.
I would like to count how many row are inside row 3, and store it in the ContainsCount column.
I know i can get this value manually by doing an additional query for every row that i need to read. But i would like to avoid the strain on the server.
I have tried setting up a trigger that increments the value every time a new row with the corresponding id is added
BEGIN
UPDATE Stock_Items Set Name = "Testing" Where ID = 2;
END
But it turns out i cannot edit the same table using a trigger.
Is there any other way to achive this?

Related

How to store status of rows in MySQL according formalization rules?

I have a table order_executors:
id | order_id | user_id | status
Where order_id and user_id are external keys.
The status columns means the state if row. It accepts 0, 1, 2:
0 - default
1 - accepted
2 - canceled
So, I discoverated the violation of consistancy when:
id | order_id | user_id | status
1 1 1 1
2 1 1 2
In case described above I see that first user accepted a row status = 1 then canceled this status = 2.
So, when business logic retrieves a data by query: SELECT * from order_executors WHERE order_id = 1 AND status = 1 it is still work, despite user has canceled this order.
I can solve this using SELECT * from order_executors WHERE order_id = 1 AND status = 1 AND status !== 2.
Exactly, I can use UPDATE instead INSERT to store the current state of status.
But in this case I lost history of order_executors.status.
How to solve this design issue?
My idea is to create a new table order_executors_history where to store status changes:
id | user_id | order_id | status
And inside order_executors store the current state using UPDATE command.
You already gave the solution.
Just update the status (or delete and insert the new row) on table order_executors, keeping only one row per order_id | user_id keys;
Create another table order_executors_history and when you insert/update on table order_executors you should make an insert into order_executors_history.
Both tables having the same structure.
You will have the status already ordered by your id inside table order_executors_history.
I prefer to keep history in another table with a column that indicates the date and time for changes.
but if you want to keep them in the same table, I think you can add a column with type of int(11) and default value of "unix_timestamp(current_timestamp())". then you can query the last status of order by selecting maximum value of the timestamp_field or by sorting query result after "where clause" in descending order and limit it to one row.

How to copy a table (MySQL) and automatically update the new one?

I want to create a small copy of a bigger table and link both of them.
Every time I make an update in the bigger one, I want that the small one updates too.
For example, I have this data:
Big table:
id | name | price
1 | a | 10
2 | b | 12
Small table:
id | name
1 | a
2 | b
---- UPDATING THE BIGGER ONE ---
Big table:
id | name | price
1 | y | 10
2 | b | 12
3 | c | 13
Small table should become AUTOMATICALLY (after I update the bigger one):
id | name
1 | y
2 | b
3 | c
Do you know how to do it?
To do exactly what you're asking you could use triggers. Triggers are SQL that execute automatically when certain events happen. To mirror the data you would need to create UPDATE, INSERT, and DELETE triggers. (I don't have access to a MySQL instance at the moment to test, so there could be typos)
CREATE TRIGGER big_to_small_insert
AFTER INSERT ON big
FOR EACH ROW
INSERT INTO small (id, name) VALUES (NEW.id, NEW.name)
CREATE TRIGGER big_to_small_update
AFTER UPDATE ON big
FOR EACH ROW
UPDATE small SET name = NEW.name WHERE id = NEW.id
CREATE TRIGGER big_to_small_delete
AFTER DELETE ON big
FOR EACH ROW
DELETE FROM small WHERE id = OLD.id
However, a View is probably a better option if the "small" table is truly just the big table with a subset of data. A View won't store a copy of the data, so if you update the table (big) it will be reflected in the view (small), but the opposite is also true. If you do an INSERT, UPDATE, or DELETE on the view (small) it will actually happen in the table (big).
CREATE VIEW small AS
SELECT id, name FROM big

Run UPDATE query by a custom order

Is there a way to update all records in the table by a specific custom order? I specifically mean a situation, when the actual order comes from the 'outside' (eg as POST value).
For example, have a table
id | title | order_idx
----------------------
1 | lorem | 1
2 | ipsum | 2
3 | dolor | 3
I have a form that submits a hidden field, carrying ID values in this order: 2, 3, 1
I want to update the table to add incremental number to order_idx in each next row, going by the ID order served by the form field. So in this case, end result should look like this:
id | title | order_idx
----------------------
1 | lorem | 3
2 | ipsum | 1
3 | dolor | 2
Can this be done in a single UPDATE query somehow as opposed to running 3 queries (each including WHERE clause) in a php loop
You can use conditional expressions in assignment statements, like so:
UPDATE t
SET x = CASE
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 1 THEN 3
ELSE x
WHERE ....
parameterized:
UPDATE t
SET x = CASE
WHEN ? THEN 1
WHEN ? THEN 2
WHEN ? THEN 3
ELSE x
WHERE ....
In either case, the query will most likely need constructed dynamically to account for a varying number of items to order.
Since your comment indicates potentially hundreds...
MySQL has a limit to query length (reference).
For a large number, I would start recommending a different approach.
Step 1) CREATE TEMPORARY TABLE `newOrder` (id INT, new_order_idx INT);
Step 2) INSERT id's and their new order into the temp table.
Step 3) UPDATE t INNER JOIN newOrder AS n ON t.id = n.id SET t.order_idx = n.new_order_idx WHERE ...
Step 4) DROP TEMPORARY TABLE newOrder;
The process itself is no longer a single query; but the UPDATE is.
Note: If you have a unique key involving order_idx I am entirely sure either of these would work. Occasions when I have needed to maintain uniqueness, the usual solution is to shift the records to be adjusted to a completely different range in one step, and then to their new positions in a second one. (Something like UPDATE t SET order_idx = -1 * order_idx WHERE ... would work as a pre-Step 3 range shift in the second part of this answer.)
Use ON DUPLICATE KEY UPDATE like below:
INSERT INTO table (id, order_idx) VALUES (1,3),(2,1),(3,2)
ON DUPLICATE KEY UPDATE order_idx=VALUES(order_idx);
So you can set order_idx dynamically for every row:
INSERT INTO table (id, order_idx)
VALUES (1,order_list[0]),(2,order_list[1]),(3,order_list[2])
ON DUPLICATE KEY UPDATE order_idx=VALUES(order_idx);

Add from the 5th value

The goal
Add from the 5th value.
The problem
There is a table on my database called markets. Within it, there are 5 values:
+----+------+
| Id | Name |
+----+------+
| 0 | NULL |
+----+------+
| 1 | A |
+----+------+
| 2 | B |
+----+------+
| 3 | C |
+----+------+
| 4 | D |
+----+------+
Id is a primary key and auto-incremented column and its start by zero (I added it manually to use as default). When I add some market to this table, seems that MySQL adds the market in question with 0 as Id — and I want to set the Id as 5 to continue the table's logic. So I ask: how can I add some value from the 5th Id?
What I've already tried
I already set the auto_increment value for 5, but without success. The query that I used is the following:
ALTER TABLE `markets` AUTO_INCREMENT = 5;
How do I know that the MySQL is attempting to add the market with its Id as 0?
My application is powered by C# that is throwing an exception:
Duplicate entry '0' for key 'PRIMARY'
On your INSERT statement, leave the auto_increment field out of the statement. This will leave MySQL to increment it and add it automatically.
how can I add some value from the 5th Id
Just try:;--
INSERT INTO markets(Name) VALUES('E');
ie, dont use the auto_increment field while inserting the data. MySQL is smart enough to do that for you ;)
Already there are four values are there in the table, So you can just insert next row. it will automatically take 5 as a next value.
When we are using auto_increment no need to specify that column in the insert command.
INSERT INTO markets(Name) VALUES('E');
In case if you want to alter the auto_increment value you can use the following statement
ALTER TABLE market AUTO_INCREMENT = 5;

Mysql auto_increment proceed with lowest value

My problem is: I have a table with an auto_increment column. When I insert some values, all is right.
Insert first row : ID 1
Insert second row : ID 2
Now I want to insert a row at ID 10.
My problem is, that after this there are only rows inserted after ID 10 (which is the normal behaviour ).
But I want that the database first fills up ID 3-9 before making that.
Any suggestions?
EDIT:
To clarify: this is for an URL shortener I want to build for myself.
I convert the id to a word(a-zA-z0-9) for searching, and for saving in the database I convert it to a number which is the ID of the table.
The Problem is now:
I shorten the first link (without a name) -> ID is 1 and the automatically name is 1 converted to a-zA-Z0-9 which is a
Next the same happenes -> ID is 2 and the name is b, which is 2 converted.
Next interesting, somebody want to name the link test -> ID is 4597691 which is the converted test
Now if somebody adds another link with no name -> ID is 4597692 which would be tesu because the number is converted.
I want that new rows will be automatically inserted at the last gap that was made (here 3)
You could have another integer column for URL IDs.
Your process then might look like this:
If a default name is generated for a link, then you simply insert a new row, fill the URL ID column with the auto-increment value, then convert the result to the corresponding name.
If a custom name is specified for a URL, then, after inserting a row, the URL ID column would be filled with the number obtained from converting the chosen name to an integer.
And so on. When looking up for integer IDs, you would then use the URL ID column, not the table auto-increment column.
If I'm missing something, please let me know.
You could do 6 dummy inserts and delete/update them later as you need. The concept of the auto increment, by design, is meant to limit the application's or user's control over the number to ensure a unique value for every single record entered into the table.
ALTER TABLE MY_TABLE AUTO_INCREMENT = 3;
You would have to find first unused id, store it as user variable, use as id for insert.
SELECT #id := t1.id +1
FROM sometable t1 LEFT JOIN sometable t2
ON t2.id = t1.id +1 WHERE t2.id IS NULL LIMIT 1;
INSERT INTO sometable(id, col1, col2, ... ) VALUES(#id, 'aaa', 'bbb', ... );
You will have to run both queries for every insert if you still have gaps, its up to you to decide whether it is worth doing it.
not 100% sure what you're trying to achieve but something like this might work:
drop table if exists foo;
create table foo
(
id int unsigned not null auto_increment primary key,
row_id tinyint unsigned unique not null default 0
)
engine=innodb;
insert into foo (row_id) values (1),(2),(10),(3),(7),(5);
select * from foo order by row_id;
+----+--------+
| id | row_id |
+----+--------+
| 1 | 1 |
| 2 | 2 |
| 4 | 3 |
| 6 | 5 |
| 5 | 7 |
| 3 | 10 |
+----+--------+
6 rows in set (0.00 sec)