Modify order field - mysql

I have a table like this
+----+-------+---------+
| id |order | name |
+----+-------+---------+
| 1 | 2 | John |
| 2 | 1 | William |
| 3 | 4 | Karl |
| 4 | 3 | Michael |
+----+-------+---------+
I want to Move Karl from the fourth place to the second so the order field of Karl will be set to 2, John 3 and Michael 4.
Is there a way to update the table with only one query?

You can try a conditional update:
UPDATE mytable
SET myfield = CASE other_field
WHEN 1 THEN 'value1'
WHEN 2 THEN 'value2'
WHEN 3 THEN 'value3'
END
WHERE id IN (1,2,3)

If you know that Karl is already in fourth, and you know that he's moving up, then this should do it:
UPDATE mytable
SET order = (order - 1) % 3 + 2
WHERE ORDER BETWEEN 2 AND 4
For a more reusable query, if someone is moving from position B to position A in the list (A < B), you would do this (substitute real numbers for A and B):
UPDATE mytable
SET order = (order - A + 1) % (B - A + 1) + A
WHERE ORDER BETWEEN A AND B
If you want to move someone down the list, from position A to B, instead, do this:
UPDATE mytable
SET order = (order - A - 1) % (B - A + 1) + A
WHERE ORDER BETWEEN A AND B
and everyone in between will be updated appropriately.

Thank you for your answers.
They help me come up with a single query that updates the order field from any position A to any position B:
UPDATE mytable
SET order=MOD(order-LEAST(A,B)+SIGN(A-B),GREATEST(A,B)-LEAST(A,B)+1)+LEAST(A,B)
WHERE order BETWEEN LEAST(A,B) AND GREATEST(A,B)

Related

How to get column that have the same value

Sorry for my lack of knowledge in MySQL but how do you get the same value in different field?
For example if i have a table like so :
+-----+-----+-----+
| id | A | B |
+=====+=====+=====+
| 1 | 1 | 2 |
| 2 | 3 | 1 |
+-----+-----+-----+
And i would like to get the value 1 from A and B.
I tried doing something like :
SELECT A, B FROM table_name WHERE A = 1 AND B = 1
But this won't return a value if somehow A or B doesn't have the value 1. In cases like that I want it to only return the value 1 from the column that does have it. I want something like this :
SELECT A, B FROM table_name WHERE value = 1
I think you want OR:
SELECT A, B
FROM t
WHERE A = 1 OR B = 1
This can be shortened using IN:
WHERE 1 IN (A, B)
You should use query as:
SELECT A, B FROM table_name WHERE A = 1 OR B = 1
Here, you get rows which have either A=1 or B=1 or both A & B = 1.

PHPMyAdmin - Update 30k rows with looped number sequence

I hope i'm explaining this properly... but i'm trying to update a column in a table with 30k rows with a repeated sequence.
I've populated entire columns before with random numbers using:
UPDATE locations SET template = CAST((RAND() * 4)+1 AS UNSIGNED);
Which gave:
2
4
5
1
3
etc. in a random fashion throughout the 30k rows...
I would like to enter a query that can produce a repeated sequence like:
1
2
3
4
5
1
2
3
4
5
across all 30k rows.
I've been looking into loops and auto increments but can't get it to work.
Any help much appreciated :)
Perhaps using a variable will do for example
DROP TABLE IF EXISTS T;
CREATE TABLE T(ID INT, SEQNO INT);
INSERT INTO T VALUES (1,NULL),(2,NULL),(3,NULL),(4,NULL),(5,NULL),(6,NULL),(7,NULL);
UPDATE T
SET SEQNO = (SELECT IF(#RN = 2 ,#RN:=1,#RN:=#RN + 1) FROM (SELECT #RN:=0) R)
WHERE 1 = 1
+------+-------+
| ID | SEQNO |
+------+-------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 1 |
+------+-------+
Thanks for the suggestions... I had a hard time finding an answer but eventually found something that would do exactly what I was after. I must admit it is far beyond my capabilities, but here it is:
SET #row_number = 0;
SET #max_num = 75;
update locations loc1
join (
select
if ((num % #max_num) = 0, #max_num, (num % #max_num)) as num2,
a.*
from (
select
(#row_number:=#row_number + 1) AS num,
loc.*
from locations loc
ORDER BY num
) a
order by num, num2
) loc2 on (loc2.id = loc1.id)
set loc1.colname = loc2.num2;

How to pick distinct row value based on column in sql/plsql

Consider this table:
name mark1 mark2 mark3
x 1 2 2
y 2 2 2
z 1 2 3
Here, I need to select the non distinct for example consider row three, where it contains only one "2" in column2. For this how to write a SQL code? I have made it of using the count and distinct commands but not able to get it.
Try it this way
SELECT *
FROM table1
WHERE mark1 <> mark2
AND mark1 <> mark3
AND mark2 <> mark3;
Output:
| NAME | MARK1 | MARK2 | MARK3 |
|------|-------|-------|-------|
| z | 1 | 2 | 3 |
Here is SQLFiddle demo
If I understand your question enough, it would be like this:
select * from mark where (mark1<>mark2 and mark1<>mark3 and mark2<>mark3);
in case you need to select all columns contain values that are unequal
like in this case row3
SELECT *
FROM table a
WHERE a.m1<>a.m2
AND a.m2<>a.m3
AND a.m1<>a.m3
see fiddle here

SQL query taking "too" long - select with distinct and max

I have the scenario:
A table with POSITION and a value associated for each position, BUT I have always 4 values for the same position, so an example of my table is:
position | x| values
1 | x1 | 0
1 | x2 | 1
1 | x3 | 1.4
1 | x4 | 2
2 | x1 | 3
2 | x2 | 10
2 | x3 | 12.4
2 | x4 | 22
I need a query that returns me the MAX value for each unique position value. Now, I am querying it with:
SELECT DISTINCT (position) AS p, (SELECT MAX(values) AS v FROM MYTABLE WHERE position = p) FROM MYTABLE;
It took me 1651 rows in set (39.93 sec), and 1651 rows is just a test for this database (it probably should have more then 1651 rows.
What am I doing wrong ? are there any better way to get it in a faster way ?
Any help is appreciated.
Cheers.,
Use the GroupBy-Clause:
SELECT Position, MAX(VALUES) FROM TableName
GROUP BY Position
Also, have a look at the documentation (about groupby):
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Try using the GROUP BY clause:
SELECT position AS p, MAX(values) AS v
FROM MYTABLE
GROUP BY p;
try this
SELECT position AS p, MAX(`values`) AS v FROM MYTABLE
GROUP BY position
DEMO HERE
OUTPUT:
P | V
1 | 2
2 | 22

MySQL: select one row in subquery similar to MAX, only with custom order priority

Say I have two tables a and b; the column id is unique in table a but has multiple entries in table b, with different values for column x as well as other columns in that table. The primary key of table b is (id,x).
I need to be able to join a single row from b to the SELECT query as I could do with MAX like so:
SELECT * FROM a
INNER JOIN b USING(id)
WHERE b.x = (SELECT MAX(x) FROM b WHERE b.id = a.id)
With MAX, this works no problem. But I don't need MAX. I actually need something like this:
SELECT * FROM a
INNER JOIN b USING(id)
WHERE b.x = (SELECT x FROM b
WHERE x IN (2,3,7) OR x IS NULL
ORDER BY FIELD(x,3,2,7) DESC
LIMIT 1)
This query fails because my MySQL version does not support LIMIT in this subquery. Is there another way to make sure to join at least and exactly one row in the order I provided?
Schematic overview of what I'm trying to do would be:
Select * from table a for each id
Join table b where x = 7
If no entry in b exists where a.id=b.id and x = 7, join b where x = 2
If no entry in b exists where a.id=b.id and x IN (2,7), join b where x = 3
If no entry in b exists where a.id=b.id and x IN (2,3,7), join b where x IS NULL
I have this need because:
I can't use more than one query in this particular bit of code I need; it has to be one magic all-in-one query
I can't use INNER JOIN (SELECT *) statements
I can't have a query where any id is present more than once (so DISTINCT is not option because the values in table b might differ)
I know for sure that table b has an entry for a.id where x is either 2, 3, 7 or NULL, but I don't know which and I also don't know how many entries there are for a.id in table b.
Upgrading MySQL is not the best option since this code would have to work on generic servers of any of my customers
So, in short, my question is: is there a function similar to MAX that can take a specific order into account instead of the MAX value?
Thanks in advance!
You can do this by specifying manually the ordering weight of each X.
mysql> select * from aa;
+----+------+
| id | name |
+----+------+
| 1 | John |
| 2 | Ted |
| 3 | Jill |
| 4 | Jack |
+----+------+
4 rows in set (0.00 sec)
mysql> select * from bb;
+------+------+------------+
| id | x | class |
+------+------+------------+
| 1 | 7 | HighPriori |
| 1 | 2 | MediumPrio |
| 1 | 3 | LowPriorit |
| 2 | 2 | Medium |
| 2 | 3 | Low |
| 3 | 3 | Low only |
+------+------+------------+
5 rows in set (0.00 sec)
select version();
+-------------+
| version() |
+-------------+
| 5.5.25a-log |
+-------------+
SELECT aa.name, bb.x, bb.class
FROM aa LEFT JOIN bb ON (aa.id = bb.id AND bb.x IN (2,3,7)
AND bb.x = ( SELECT x FROM bb WHERE bb.id = aa.id AND x IN (2,3,7)
ORDER BY CASE
WHEN x = 7 THEN 100
WHEN x = 2 THEN 200
WHEN x = 3 THEN 300
ELSE 400
END LIMIT 1 )
);
The innermost SELECT will choose the most suitable value of X based on priority: 7 if available, else 2, else 3. This works also if, as in this case, the "priorities" are not in order, i.e., 7 is higher than 3, but 2 is also higher than 3.
Then the LEFT JOIN will match that one record, if it exists, or NULL, if it does not.
John has 2,3 and 7 and gets the 7-record, while Ted has 2 and 3, and gets the 2:
+------+------+------------+
| name | x | class |
+------+------+------------+
| John | 7 | HighPriori |
| Ted | 2 | Medium |
| Jill | 3 | Low only |
| Jack | NULL | NULL |
+------+------+------------+
(Strictly speaking, the IN (2,3,7) in the inner SELECT and the ELSE in the CASE are redundant; either will do).
In CASE of need...
If the X field already establishes an order, e.g., you want the values 3,2,7 in either 2 - 3 - 7 or 7 - 3 - 2 numeric order, you can do without the CASE: instead of
ORDER BY CASE
WHEN x = 7 THEN 100
WHEN x = 2 THEN 200
WHEN x = 3 THEN 300
ELSE 400
END
you can just specify
ORDER BY x
or
ORDER BY x DESC
...the performance improvement is slight, even if x is indexed on, but if you have a great many values of X, then specifying them all in the CASE can be awkward, and studying exceptions may be lengthy.
But let's imagine you wanted the objects to be in this order
First Last
20,21,22,23,40,41,42,9,1,2,3,4,5
which typically arises when X is declared unsigned with values 1-5 ("we will never need more classes and 1 is always going to be first!"), then some weeks later someone adds a "this is a new exciting product, must go first!" exception and assigns 9 to it, and finally it happens again with "let's add a whole new XY class starting with 2X and 4X for the forthcoming product series...", you could do this as
WHEN x <= 5 THEN 900+x
WHEN x = 9 THEN 809
ELSE 700 + x
which translates the above jumbled set in the ordered sequence
720,721,722,723,740,741,742,809,901,902,903,904,905
and with three WHENs you do all the work.
SELECT * FROM a
INNER JOIN b USING(id)
WHERE b.x = (SELECT x FROM b
WHERE x IN (2,3,7) OR x IS NULL
ORDER BY FIELD(x,3,2,7) DESC
LIMIT 1)
switch the subquery to a join
SELECT * FROM a
join ( SELECT x FROM b
WHERE x,id IN (2,3,7) OR x IS NULL
ORDER BY FIELD(x,3,2,7) DESC
LIMIT 1) as X on X.id = a.id