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

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

Related

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;

Modify order field

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)

How to set mysql datediff result into row list

I would like to ask you guys about something about DateDiff in MySQL.
for example, here is some code of mysql datediff.
select datediff('2015-10-11', '2015-10-15') as Diffdate
and the result would be
| DiffDate |
------------
| 4 |
------------
so, my question is, how do I make the Diffdate result into some kind of row numbers from 1 to the result of diffDate?
this is the result that I want.
| DiffDate |
------------
| 1 |
| 2 |
| 3 |
| 4 |
------------
thank you in advance
The trick is first to select numbers from 1 to 31:
select n.x from
(Select 1 x
union select 2 x
union select 3 x
...
union select 31 x) n ,
(select datediff('2015-10-15', '2015-10-11') as Diffdate) d
where n.x <= d.Diffdate
I the example above should datediff('2015-10-11', '2015-10-15') is -4: that's why i have exchanged the args order.

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

MySQL: How do I add a string on row X2 with ID Y1 in column Z1 to row X1 with ID Y1 to column Z1?

TABLE A
Row IdA ValueA
1 1 ABCD
2 2 EFGH
3 3 IJKL
TABLE B
Row IdB ValueB
1 1 QWER
2 2 TYUI
3 3 OPAS
CONNECTOR X
Row IdA IdB
1 1 1
2 1 2
3 2 3
I want the output to display:
OUTPUT
Value A --- ValueB(1), ValueB(2)
ABCD --- QWER, TYUI
So, basically, every time there's a doublet in the connector table's IdA column, those two (or more) entries merge the strings in the Value field for my output.
Is this even doable with a MySQL query, or do I -have- to resort to sorting through the whole database with a PHP array? I'd rather like to avoid that, if at all possible!
I've looked at the various JOINs to no avail and thought about using a GROUP BY and COUNT(DISTINCT ...) query, but it just seems a very inelegant way to go about it. Suggestions are welcome!
SELECT a.ValueA, GROUP_CONCAT( b.ValueB SEPARATOR ', ' ) AS ValuesB
FROM connector c
JOIN tblA a ON c.IdA = a.IdA
JOIN tblB b ON c.IdB = b.IdB
GROUP BY a.IdA
Will give you results:
+--------+------------+
| ValueA | ValuesB |
+--------+------------+
| ABCD | QWER, TYUI |
| EFGH | OPAS |
+--------+------------+