Make new column which is incremented by it's order - mysql

I need to make new column for my table Products -> called Order (new column). And using rails migration I need to add new column and instantly set it's order number, but it's need to be done by product_id.
What I mean I need something like:
product_id | order
1 ------------> 1
1 ------------> 2
1 ------------> 3
2 ------------> 1
2 ------------> 2
Is there a way of doing it?
EDIT :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''order' = t1.'order'' at line 15:
update product_submissions t
join (
select
id,
product_id,
'order' from (
select id,
product_id,
#rn:= if(#prev = product_id,#rn:=#rn+1,1) as 'order',
#prev:=product_id
from product_submissions,
(select #rn:=0,#prev:=0)r
order by product_id,id
)x
)t1 on t1.id=t.id set t.'order' = t1.'order'

Consider the following
mysql> create table test (id int ,product_id int);
Query OK, 0 rows affected (0.14 sec)
mysql> insert into test values (1,1),(2,1),(3,1),(4,2),(5,2);
Now lets create the order
select
product_id,
`order` from (
select
product_id,
#rn:= if(#prev = product_id,#rn:=#rn+1,1) as `order`,
#prev:=product_id from test,(select #rn:=0,#prev:=0)r
order by product_id,id
)x ;
This will give you something as
+------------+-------+
| product_id | order |
+------------+-------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+------------+-------+
Now lets use in update command, but before that lets add the column (in your case its already there)
mysql> alter table test add column `order` int ;
Query OK, 5 rows affected (0.29 sec)
mysql> select * from test ;
+------+------------+-------+
| id | product_id | order |
+------+------------+-------+
| 1 | 1 | NULL |
| 2 | 1 | NULL |
| 3 | 1 | NULL |
| 4 | 2 | NULL |
| 5 | 2 | NULL |
+------+------------+-------+
5 rows in set (0.00 sec)
Finally the update command
update test t
join (
select
id,
product_id,
`order` from (
select id,
product_id,
#rn:= if(#prev = product_id,#rn:=#rn+1,1) as `order`,
#prev:=product_id
from test,(select #rn:=0,#prev:=0)r
order by product_id,id
)x
)t1 on t1.id=t.id set t.`order` = t1.`order`
mysql> select * from test ;
+------+------------+-------+
| id | product_id | order |
+------+------------+-------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 5 | 2 | 2 |
+------+------------+-------+
5 rows in set (0.00 sec)

Related

MYSQL: How to fill null values in column with the previous entry?

I've got a program at work that exports to CSV but leaves blanks in the most irritable places. I want to view the carrier and destination on the same row and currently the carrier is 1 row above the destination like below:
I have a database that is like the following:
|Key|Carrier ||Destination|
|-------------------------|
| 1 | HULL2 || |
| 2 | || C14A102 |
| 3 | DONC1 || |
| 4 | || D15A012 |
What I want:
|Key|Carrier ||Destination|
|-------------------------|
| 1 | HULL2 || |
| 2 | HULL2 || C14A102 |
| 3 | DONC1 || |
| 4 | DONC1 || D15A012 |
Either that or insert a new column with the information from carrier column.
Sorry if this is confusing its confusing me to explain it!
James
Here is a solution, by cloning another table and then deleting it:
CREATE TABLE t1(Key_id INT PRIMARY KEY, Carrier CHAR(20), Destination CHAR(20));
INSERT INTO t1 VALUES(1, 'HULL2', ''),(2,'','C14A102'),(3,'DONC1',''),(4,'','D15A012');
CREATE TABLE t2 LIKE t1;
INSERT INTO t2 SELECT * FROM t1;
SELECT * FROM t1;
UPDATE t1 SET Carrier =
(
SELECT t2.Carrier
FROM t2
WHERE t2.Key_id < t1.Key_id AND t2.Carrier != ''
ORDER BY t2.Key_id DESC
LIMIT 1
)
WHERE Carrier = '';
SELECT * FROM t1;
DROP TABLE t2;
Output:
mysql> SELECT * FROM t1;
+--------+---------+-------------+
| Key_id | Carrier | Destination |
+--------+---------+-------------+
| 1 | HULL2 | |
| 2 | | C14A102 |
| 3 | DONC1 | |
| 4 | | D15A012 |
+--------+---------+-------------+
4 rows in set (0.00 sec)
mysql> UPDATE t1 SET Carrier =
-> (
-> SELECT t2.Carrier
-> FROM t2
-> WHERE t2.Key_id < t1.Key_id AND t2.Carrier != ''
-> ORDER BY t2.Key_id DESC
-> LIMIT 1
-> )
-> WHERE Carrier = '';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> SELECT * FROM t1;
+--------+---------+-------------+
| Key_id | Carrier | Destination |
+--------+---------+-------------+
| 1 | HULL2 | |
| 2 | HULL2 | C14A102 |
| 3 | DONC1 | |
| 4 | DONC1 | D15A012 |
+--------+---------+-------------+
4 rows in set (0.00 sec)
Assuming that the column 'key' can be trusted in this way, I would update with a self join where the join uses key = key+1, and then make sure it's only affecting even rows.
UPDATE tablename as even_row JOIN tablename as odd_row
ON even_row.Key = odd_row.Key + 1
SET even_row.Carrier = odd_row.Carrier
WHERE odd_row.Key % 2;

mysqli SUM and MAX results

hi guys im having trouble understanding how to get a MAX() and a SUM() from a Database
my database have 3 colums id - stuff - score looks like this
id-stuf-score
1-1-2
1-1-3
1-2-1
1-2-3
1-3-1
1-3-3
i need to make a
MAX(score) where stuff=1 and id = 1
MAX(score) where stuff=2 and id = 1
MAX(score) where stuff=3 and id = 1
.
.
etc
and then a
SUM(score) all the MAX(score)
i have hundred of rows and i cant think of a way to make it happens
make subquery table derivatives for each thing you need the max of, then add them all together.
SELECT SUM(a.max+b.max+c.max) as total
FROM (
(
SELECT MAX(score) as max
FROM tablename
WHERE stuff = 1 AND id = 1
) as a,
(
SELECT MAX(score) as max
FROM tablename
WHERE stuff = 2 AND id = 1
) as b,
(
SELECT MAX(score) as max
FROM tablename
WHERE stuff = 3 AND id = 1
) as c
);
Output:
_____________
| total |
+-----------+
| 12345 |
+-----------+
Try using group by to get your max scores, then sum the results.
Records in Table
I added a few more records to so the max(score) wouldn't be the same for every stuf.
mysql> select * from stuff;
+------+------+-------+
| id | stuf | score |
+------+------+-------+
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 1 | 2 | 1 |
| 1 | 2 | 3 |
| 1 | 3 | 1 |
| 1 | 3 | 3 |
| 1 | 3 | 4 |
| 1 | 3 | 1 |
| 1 | 2 | 5 |
| 1 | 2 | 1 |
+------+------+-------+
10 rows in set (0.00 sec)
Group By
mysql> select id, max(score) as max_score from stuff group by stuf;
+------+-----------+
| id | max_score |
+------+-----------+
| 1 | 3 |
| 1 | 5 |
| 1 | 4 |
+------+-----------+
3 rows in set (0.00 sec)
Sum It Up
mysql> select sum(max_score) from (select id, max(score) as max_score
from stuff group by stuf) sum_me;
+----------------+
| sum(max_score) |
+----------------+
| 12 |
+----------------+
1 row in set (0.00 sec)
One Note
Keep in mind that if you have more ids then you will need to limit your query to whichever id you are interested in or appropriately group the last query.
mysql> select id, sum(max_score) from (select id, max(score) as max_score
from stuff group by id, stuf) sum_me group by id;
you can try this
SELECT SUM(a.max) as total
FROM (
(
SELECT MAX(score) as max
FROM tablename
GROUP BY stuf, id
) as a
);

MySQL riddle with subquery

EDIT: looks like an index issue, update at the bottom of the question
I have the following query + subquery whose results I cannot explain. I am starting with this minimal input data set (the application here is capturing data change, and the PK is the id + the tx_id).
mysql> select * from tag_version;
+----+-------------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------------+------------+-------+----------------+
| 1 | some tag | 1 | 1 | 0 |
| 1 | updated tag | 1 | 2 | 1 |
| 1 | updated again tag | 1 | 3 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------------+------------+-------+----------------+
4 rows in set (0.00 sec)
The subquery, standalone
SELECT max(f.tx_id) as max_tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
Result is
+-----------+----+
| max_tx_id | id |
+-----------+----+
| 2 | 1 |
| 2 | 2 |
+-----------+----+
2 rows in set (0.00 sec)
The query, where I manually inject the subquery results, notice how they are equal to the above
select t.*
from tag_version t
where t.article_id = 1
AND (t.tx_id, t.id) IN (
(2,1),
(2,2)
)
With expected results
+----+-------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------+------------+-------+----------------+
| 1 | updated tag | 1 | 2 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------+------------+-------+----------------+
2 rows in set (0.00 sec)
And lastly, using the subquery in place of the tuples...
select t.*
from tag_version t
where t.article_id = 1
AND (t.tx_id, t.id) IN (
SELECT max(f.tx_id) as tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
)
The result is Empty set (0.00 sec)! Can someone explain this? I get the same empty results when I re-write the query using EXISTS instead of IN
I noticed than when I remove the line WHERE f.tx_id <= 2 from the subquery, I actually get results (although the wrong ones):
+----+-------------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------------+------------+-------+----------------+
| 1 | updated again tag | 1 | 3 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------------+------------+-------+----------------+
2 rows in set (0.00 sec)
Replacing the subquery with a JOIN actually returns the expected correct results
SELECT t.*
FROM tag_version t
JOIN (
SELECT max(f.tx_id) as max_tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
) as max_ids
ON max_ids.max_tx_id = t.tx_id
AND max_ids.id = t.id
where t.article_id = 1
Result:
+----+-------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------+------------+-------+----------------+
| 1 | updated tag | 1 | 2 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------+------------+-------+----------------+
2 rows in set (0.00 sec)
In addition, running the same query+subquery on the same data set with both PostgreSQL and SQLite gives the expected correct results.
My MySQL version is Server version: 5.5.40-0ubuntu0.14.04.1 (Ubuntu).
I think the clue to figuring out what's happening is that I actually get results when I remove the WHERE from the subquery, but I can't make something useful out of it.
EDIT: updated with input data set
EDIT: add table information
The table create statement is as follows
CREATE TABLE `tag_version` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`article_id` int(11) DEFAULT NULL,
`tx_id` bigint(20) NOT NULL,
`operation_type` smallint(6) NOT NULL,
PRIMARY KEY (`id`,`tx_id`),
KEY `ix_tag_version_operation_type` (`operation_type`),
KEY `ix_tag_version_tx_id` (`tx_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Data population as well
insert into tag_version (id, name, article_id, tx_id, operation_type) VALUES
(1, 'some tag', 1, 1, 0),
(1, 'updated tag', 1, 2, 1),
(1, 'updated again tag', 1, 3, 1),
(2, 'other tag', 1, 2, 0)
;
When I remove the ix_tag_version_tx_id index, the query returns the correct results... An explanation of why would be useful.
I believe you made a mistake in showing the result of first code(subquery).
The output of this query:
SELECT max(f.tx_id) as qwer, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
-is not:
+--------------+----+
| max(f.tx_id) | id |
+--------------+----+
| 2 | 1 |
| 2 | 2 |
+--------------+----+
It is:
+------+----+
| qwer | id |
+------+----+
| 2 | 1 |
| 2 | 2 |
+------+----+
(*Notice: the code line max(f.tx_id) as qwer *)
Now try this code for the expexted output.
There is a change when selecting max(f.tx_id).
select t.*
from tag_version t
where t.`article_id` = 1
AND t.`operation_type` != 2
AND (t.`tx_id`, t.`id`) IN (
SELECT max(f.`tx_id`) as `tx_id`, f.`id`
from tag_version f
WHERE f.`tx_id` <= 2
GROUP BY f.`id`
)
Let me know if this gives you the result or any other error.

Complex sql, get rows only if date is older and group by 3 columns

I was wondering if this query if possible. I have this table:
+----+-------+--------+--------+------------+
| id | plate | agency | status | assigned |
+----+-------+--------+--------+------------+
| 1 | UB10 | 0 | 3 | 2010-01-02 |
| 2 | UB10 | 2 | 2 | 2010-01-03 |
| 3 | UB10 | 5 | 1 | 2010-01-04 |
+----+-------+--------+--------+------------+
In this way everything is OK and I use all the records. The main issue is the following situation:
+----+-------+--------+--------+------------+
| id | plate | agency | status | assigned |
+----+-------+--------+--------+------------+
| 1 | UB12 | 0 | 3 | 2010-01-02 |
| 2 | UB12 | 2 | 1 | 2010-01-03 |
| 3 | UB12 | 5 | 2 | 2010-01-04 |
+----+-------+--------+--------+------------+
I need a sql query where I'm able to skip the records with the status = 1 only if I have records with the same plate and there is a newest record with different status. In the above example I only need the first and third record.
It's possible?
Thanks very much.
EDITED FOR GRAMMAR
you can do this with a prepared statement like so
SET #sql = "CREATE VIEW v_test AS (SELECT * FROM my_table WHERE status <> 1)";
SET #union = " UNION (SELECT * FROM my_table ORDER BY assigned DESC LIMIT 1)";
SELECT #final := case status WHEN 1 then CONCAT(#sql, #union) ELSE #sql END
FROM
( SELECT status
FROM my_table
WHERE id =
( SELECT id FROM my_table
ORDER BY assigned DESC LIMIT 1
)
) t;
PREPARE stmt FROM #final;
EXECUTE stmt;
SELECT * FROM v_test;
DEMO WHERE LAST ROW HAS STATUS <> 1
DEMO WHERE LAST ROW HAS STATUS = 1
DEMO WITH A VIEW

Mysql Group by second column if first column is equal?

I have a table that looks like this
id | rating
1 | 1
1 | 3
1 | 1
1 | 2
2 | 3
2 | 3
2 | 1
etc,you get the idea.
Anyway, I want to end up with this result set
id | rating | num
1 | 1 | 2
1 | 3 | 1
1 | 2 | 1
2 | 3 | 2
2 | 1 | 1
So, what I want to do is group the rating's together as long as the id is the same!
I thought I could just do group by id, rating and just assumed it would group only if both columns were the same, but it doesn't it groups if either column is the same so I end up with
id | rating | num
1 | 1 | 7
How can I solve this?
I don't understand the issue you are having. This query works for me:
select
id, rating, COUNT(rating) as num
FROM rating
GROUP BY id, rating
ORDER BY id, rating ASC;
And, just to be thorough, here is my entire test session:
mysql> create table rating (id int, rating int);
Query OK, 0 rows affected (0.04 sec)
mysql> insert into rating values (1,1),(1,3),(1,1),(1,2),(2,3),(2,3),(2,1);
Query OK, 7 rows affected (0.00 sec)
Records: 7 Duplicates: 0 Warnings: 0
mysql> select id, rating, COUNT(rating) as num FROM rating GROUP BY id, rating;
+------+--------+-----+
| id | rating | num |
+------+--------+-----+
| 1 | 1 | 2 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 1 | 1 |
| 2 | 3 | 2 |
+------+--------+-----+
5 rows in set (0.00 sec)
you could try
SELECT id,rating,COUNT(rating) AS num
FROM table1
GROUP BY id,rating
ORDER BY id ASC
SQLFiddle Demo
SELECT id,rating,count(rating) from table group by id,rating