MySQL, Insert Row in-between two others - mysql

I'm coding a website for a photographer and I'm currently working on gallery implimentation.
I need to be able to take a row from point n.a and move it to point n.b
Here's an example of the raw table:
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 1 | 16 | 322 | 2 |
| 2 | 27 | 240 | 1 |
| 3 | 16 | 245 | 3 |
| 4 | 16 | 210 | 4 |
| 5 | 27 | 530 | 2 |
All fields are INT(11). 'id' Auto_increments. 'fk_gal' and 'fk_img' are linked to other, irrelevant, tables via FOREIGN_KEY.
Now, 'o' is the field I'm focusing on. It determines what order the images will be displayed on the website. This value needs to always be unique for each table. To clarify, If I only call one table, 'o' should be different in every row. However, if I call the entire table, 'o[0]' might reoccur a few times.
So here's what I need. Firstly, I'm only going to be running this function on only one gallery at a time so all visuals of the table from here on out are going to be filtered with 'SELECT * FROM gallery_img WHERE fk_gal = 16'.
I need to change 'o' from n to n2 which will effectively move it on the database.
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| | | | | <--
| 1 | 16 | 322 | 2 |+ |
| 3 | 16 | 245 | 3 |+ |
| 4 | 16 | 210 | 4 | --|
The code needs to move the desired row (in this example 'o=4') to 1 and simultaneously move all of the next rows down to prevent any reoccurrences.
Here's my code I have right now. I'm coding my MySql scripts via PHP.
I am using the $n variable here. It includes the following data:
$n = array(gallery_id,img_id,target_o);
sql("UPDATE gallery_img SET o = o + 1 ORDER BY o ASC LIMIT ". ($n[2] - 1) .", 18446744073709551615;");
sql("UPDATE gallery_img SET o = ". ($n[2] + 2) ." WHERE fk_img = $n[1] AND fk_gal = $n[0];");
The problem I'm having with this is that when I execute it I get one of these two outputs:
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 4 | 16 | 210 | 1 | <-- Shouldn't be duplicate
| 1 | 16 | 322 | 2 |
| 3 | 16 | 245 | 3 |
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 4 | 16 | 210 | 2 |
| 1 | 16 | 322 | 4 |-|
| 3 | 16 | 245 | 4 | |-- Shouldn't be duplicate
| 5 | 16 | 273 | 4 | |
| 6 | 16 | 14 | 4 |-|
A good way to think of it is as so:
UPDATE
If you have any questions please let me know!
Thanks ahead of time for your help!

So if you're wanting to change the row WHERE o=4 to o=1 then increment the number to be replaced and all greater numbers.
UPDATE gallery_img SET o = (o + 1) WHERE o >= 1
Then update the row that you want to be o=1:
UPDATE gallery_img SET o = 1 WHERE fk_img = something1 AND fk_gal = something2
Or if you only know the o use o=(4+1) since it changed in the last UPDATE:
UPDATE gallery_img SET o = 1 WHERE o = 5

Can I suggest a hack? For the column o don't use an integer number, but a DOUBLE PRECISION one.
It would be much easier to insert a row in between, just by averaging the values of the previous and next row. If you need to insert between 3 and 4, you can just insert a row with 3.5.
Of course, after some time (after 50 times at least) you would like to re-number those values, since a DOUBLE PRECISION has 53 bits for the mantissa.

Related

Precalculate numbers of records for each possible combination

I have a mySQL database table containing cellphones information like this:
ID Brand Model Price Type Size
==== ===== ===== ===== ====== ====
1 Apple A71 3128 A 40
2 Samsung B7C 3128 B 20
3 Apple ZX5 3128 A 30
4 Huawei Q32 2574 B 40
5 Apple A21 2574 A 25
6 Apple A71 3369 A 30
7 Samsung A71 7413 C 40
Now I want to create another table, that would contain counts for every possible combination of the parameters.
Params Count
============================================== =======
ALL 1000000
Brand(Apple) 20000
Brand(Apple,Samsung) 40000
Brand(Apple),Model(A71) 7100
Brand(Apple),Type(A) 6000
Brand(Apple),Model(A71,B7C),Type(A,B) 7
Model(A71) 12514
Model(A71,B7C) 26584
Model(A71),Type(A) 6521
Model(A71),Type(A,B) 8958
Model(A71),Type(A,B),Size(40) 85
And so on for every possible combination. I was thinking about creating a stored procedure (that i would execute periodically), that would perform queries with every existing condition like that, but I am a little stuck on how exactly should it look like. Or is there a better way how to do this?
Edit: the reason why I want to store information like this is to be able to show number of results in filter in client application, like in the picture.
I would like to create index on the Params column to be able to get the Count number for given hash instantly, improving performance.
I also tried querying and caching the values dynamically, but I want to try this approach as well, so I can compare which one is more effective.
This is how I am calculating the counts now:
SELECT COUNT(*) FROM products;
SELECT COUNT(*) FROM products WHERE Brand IN ('Apple');
SELECT COUNT(*) FROM products WHERE Brand IN ('Apple', 'Samsung');
SELECT COUNT(*) FROM products WHERE Brand IN ('Apple') AND Model IN ('A71');
etc.
You can use a ROLLUP for this.
SELECT
model, type, size, COUNT(*)
FROM mytab
GROUP BY 1, 2, 3
WITH ROLLUP
With your sample data, we get the following:
| model | type | size | COUNT(*) |
| ----- | ---- | ---- | -------- |
| A21 | A | 25 | 1 |
| A21 | A | | 1 |
| A21 | | | 1 |
| A71 | A | 30 | 1 |
| A71 | A | 40 | 1 |
| A71 | A | | 2 |
| A71 | C | 40 | 1 |
| A71 | C | | 1 |
| A71 | | | 3 |
| B7C | B | 20 | 1 |
| B7C | B | | 1 |
| B7C | | | 1 |
| Q32 | B | 40 | 1 |
| Q32 | B | | 1 |
| Q32 | | | 1 |
| ZX5 | A | 30 | 1 |
| ZX5 | A | | 1 |
| ZX5 | | | 1 |
| | | | 7 |
The subtotals are present in the rows with null values in different columns, and the total is the last row where all group by columns are null.

Cross join with aggregate greatest value

I have the following table, let's call it Segments:
-------------------------------------
| SegmentStart | SegmentEnd | Value |
-------------------------------------
| 1 | 4 | 20 |
| 4 | 8 | 60 |
| 8 | 10 | 20 |
| 10 | 1000000 | 0 |
-------------------------------------
I am trying to join this table with itself, to obtain the following result set:
-------------------------------------
| SegmentStart | SegmentEnd | Value |
-------------------------------------
| 1 | 4 | 20 |
| 1 | 8 | 60 |
| 1 | 10 | 60 |
| 1 | 1000000 | 60 |
| 4 | 8 | 60 |
| 4 | 10 | 60 |
| 4 | 1000000 | 60 |
| 8 | 10 | 20 |
| 8 | 1000000 | 20 |
| 10 | 1000000 | 0 |
-------------------------------------
Basically, I would need to join every row, with every other row that comes after it, then get the MAX() of the value between each of the rows joined previously. Example: if I am joining row 1 with row 3, I would need the MAX(Value) from all of these 3 rows.
What I already done is the following query:
SELECT s1.SegmentStart, s2.SegmentEnd, GREATEST(s1.Value, s2.Value) as Value FROM Segments s1 CROSS JOIN Segments s2 ON s1.SegmentStart < s2.SegmentEnd
This query creates a similar table to the one desired, but the value fields get mixed up in the following way (I've marked between !! the row that differs):
-------------------------------------
| SegmentStart | SegmentEnd | Value |
-------------------------------------
| 1 | 4 | 20 |
| 1 | 8 | 60 |
| 1 | 10 | !20! |
| 1 | 1000000 | !20! |
| 4 | 8 | 60 |
| 4 | 10 | 60 |
| 4 | 1000000 | 60 |
| 8 | 10 | 20 |
| 8 | 1000000 | 20 |
| 10 | 1000000 | 0 |
-------------------------------------
The problem is with the GREATEST() function, because it only compares the two rows that are being joined (start-end 1-4, 8-10), and not the whole interval (in this case, it would be 3 rows, the ones with start-end 1-4, 4-8, 8-10)
How should I modify this query, or what query should I use, to get my desired result?
Additional info, that may help: the rows in the original table, are always ordered based on SegmentStart, and there can be no duplicate or missing values. Every interval between x and y will appear only once in the table, with no overlaps, and no gaps at all.
I am using Maria DB 10.3.13.
Something like this?
SELECT
s1.SegmentStart
, s2.SegmentEnd
, MAX(s.Value) as Value
FROM
Segments s1
INNER JOIN Segments s2 ON (
s2.SegmentEnd > s1.SegmentStart
)
INNER JOIN Segments s ON (
s.SegmentStart >= s1.SegmentStart
AND s.SegmentEnd <= s2.SegmentEnd
)
GROUP BY
s1.SegmentStart
, s2.SegmentEnd

MySQL: Move data from multiple rows to one row based on column value

I have MySQL table in the following format. This is an output from a program that I run and I cannot change it.
+---+------------------------+
| | A B C D E |
+---+------------------------+
| | model amz wmt abt tgt |
| 1 | c3000 100 |
| 2 | c3000 200 |
| 3 | c3000 150 |
| 4 | c3000 125 |
| 5 | A1234 135 |
| 6 | A1234 105 |
+---+------------------------+
I want to move all the rows into one single row based on the value in column 1 i.e model. The caveat is that the blank rows are not actually blank and contain a null character
DESIRED OUTPUT:
+---+-----------------------+
| | A B C D E |
+---+-----------------------+
| | model amz wmt abt tgt |
| 1 | c3000 100 200 150 125 |
| 2 | A1234 200 105 135 |
+---+-----------------------+
I tried using
select model,group_concat(wmt),group_concat(amz)
from table_name
group by model
And the output that I get is riddled with commas
+---+----------------------------------+
| | A B |
+---+----------------------------------+
| | model amz wmt |
| 1 | c3000 ,,,,100,,,, ,,,200,,,, |
| 2 | A1234 ,,200,,,,,, ,105,,,,,, |
+---+----------------------------------+
You can use TRIM and IF to convert blank values to null.
SELECT
model,
GROUP_CONCAT(IF(TRIM(wmt) = '', NULL, wmt)),
GROUP_CONCAT(IF(TRIM(amz) = '', NULL, amz))
FROM
table_name
GROUP BY model
SELECT
model,
MIN(amz) AS amz,
MIN(wmt) AS wmt,
MIN(abt) AS abt,
MIN(tgt) AS tgt
FROM
table_name
GROUP BY
model

Conditionally move MySQL data between rows in same table

Working in Redmine, I need to copy(not move) data from certain rows to other rows based on matching project id numbers with time entries.
I have included a diagram of the table "custom_values" and my understanding of the design below(CURRENT DATA):
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | |
+----+-----------------+---------------+-----------------+-------+
At the risk of oversimplifying,
"id" = The primary key for each entry in custom_values
"customized_type" = Specifies which db table the row is referring to.
"customized_id" = Specifies the primary key for the db table entry previously specified in "customized_type".
"custom_field_id" = Specifies which custom field the row is referring to. Redmine admins can arbitrarily add and remove custom fields.
"value" = The data contained within the custom field specified by
"custom_field_id"
In my situation, the values listed in "value" are representing unique customer id numbers. The customer id numbers did not always get entered with each time entry. I need to copy the customer numbers from the project rows to the matching time entry rows. Each time entry has a project_id field.
So far, here is my mangled SQL query:
SELECT
custom_field_id,
custom_values.value AS 'CUSTOMER_NUMBER',
custom_values.customized_id AS 'PROJECT_ID_NUMBER',
custom_values.customized_type,
time_entries.comments AS 'TIME_ENTRY_COMMENTS'
FROM
redmine_tweaking.custom_values
LEFT JOIN
redmine_tweaking.time_entries ON custom_values.customized_id = time_entries.project_id
WHERE
custom_values.customized_type='Project' AND custom_values.custom_field_id=1;
The query I have so far allows me to see that I have the time entries connected properly to their matching projects, but that is all I have been able to figure out. So in other words, this SQL statement does not exactly solve my problem.
Plus, even if it did work, I think the way I laid it out looks like 200 lbs of bird poop. There must be a better/more optimized way to do this.
Any help would be greatly appreciated. I am relatively new and I have been pouring hours into solving this problem.
UPDATE:
Ok, here is the time_entries table:
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| id | project_id | user_id | issue_id | hours | comments | activity_id | spent_on | tyear | tmonth | tweek | created_on | updated_on |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| 1 | 1 | 1 | 1 | .25 | test | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 08:18:12 | 2015-11-04 10:18:12 |
| 2 | 2 | 1 | 1 | .25 | test2 | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 09:18:12 | 2015-11-04 12:18:12 |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
As opposed to the original table that I first posted, the expected output would show this:
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | 03 |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | 04 |
+----+-----------------+---------------+-----------------+-------+

MySQL - Count column less than other column in same table

i want to COUNT column less than with other column which if Quantity in Hand < Minimum Quantity and i should get the result = 5
Instead, i got the result = 3.
| stock_id | stock_qtyhand | stock_minQty |
| 1 | 60 | 100 |
| 2 | 29 | 58 |
| 3 | 12 | 20 |
| 4 | 5 | 35 |
| 5 | 30 | 67 |
What seems to be the problem?
rsLowStock.Open "SELECT COUNT(stock_id) AS LowStock FROM stock_general WHERE stock_qtyhand<stock_minQty",conn
I appreciate your help