Insert ordinal number - mysql

MySQL 5.7
Consider the following sample data:
CREATE TABLE catalog_product_entity_media_gallery (
`value` VARCHAR(24),
`entity_id` INTEGER
);
INSERT INTO catalog_product_entity_media_gallery
(`value`, `entity_id`)
VALUES
('a01-some-item-p1-png.png', '1'),
('a01-some-item-p2-png.png', '1'),
('a01-some-item-d1-png.png', '1'),
('a01-some-item-d5-png.png', '1'),
('another-transparent.png', '2'),
('another-back.png', '2'),
('another-front.png', '2'),
('another-side.png', '2');
CREATE TABLE catalog_product_entity (
`entity_id` INTEGER,
`sku` VARCHAR(3)
);
INSERT INTO catalog_product_entity
(`entity_id`, `sku`)
VALUES
('1', 'a01'),
('2', 'b22');
CREATE TABLE catalog_product_entity_varchar (
`attribute_id` INTEGER,
`value` VARCHAR(24)
);
INSERT INTO catalog_product_entity_varchar
(`attribute_id`, `value`)
VALUES
('85', 'a01-some-item-p1-png.png'),
('85', 'another-transparent.png');
DB Fiddle of same: https://www.db-fiddle.com/f/7fAx1waY3TwjR34PanBkkv/0
With the query below, I get the following result:
select
a.value as 'original_file_name',
b.sku,
if(isnull(c.attribute_id), 0, 1) as 'is_default',
concat(sku, '_', if(isnull(c.attribute_id), concat('slideshow_', 'x'), 'default_1'), '.', substring_index(a.value, "." , -1)) as 'new_file_name'
from catalog_product_entity_media_gallery a
join catalog_product_entity b on b.entity_id = a.entity_id
left join catalog_product_entity_varchar c on c.attribute_id = 85 and c.value = a.value
order by sku, is_default desc;
+--------------------------+-----+------------+---------------------+
| original_file_name | sku | is_default | new_file_name |
+--------------------------+-----+------------+---------------------+
| a01-some-item-p1-png.png | a01 | 1 | a01_default_1.png |
| a01-some-item-p2-png.png | a01 | 0 | a01_slideshow_x.png |
| a01-some-item-d1-png.png | a01 | 0 | a01_slideshow_x.png |
| a01-some-item-d5-png.png | a01 | 0 | a01_slideshow_x.png |
| another-transparent.png | b22 | 1 | b22_default_1.png |
| another-back.png | b22 | 0 | b22_slideshow_x.png |
| another-front.png | b22 | 0 | b22_slideshow_x.png |
| another-side.png | b22 | 0 | b22_slideshow_x.png |
+--------------------------+-----+------------+---------------------+
In the new_file_name column, I want to insert an ordinal number in place of x. It should start at 1 for every new sku.
Wanted result:
a01_default_1.png
a01_slideshow_1.png
a01_slideshow_2.png
a01_slideshow_3.png
b22_default_1.png
b22_slideshow_1.png
b22_slideshow_2.png
b22_slideshow_3.png

Kindly try it
;with cte as (
select a.*,ROW_NUMBER() over (partition by new_file_name order by ID) as ROWNUMBER
from catalog_product_entity_media_gallery a
)
select concat(a.new_file_name,'_',ROWNUMBER) from cte

Using a variable that holds a number that increments for every row.
When is_default = 1, the number gets reset.
drop temporary table if exists pictures;
create temporary table pictures
select
a.entity_id,
a.value as original_file_name,
b.sku as sku,
if(isnull(d.attribute_id), 0, 1) as is_default,
substring_index(a.value, "." , -1) as file_extension
from catalog_product_entity_media_gallery a
join catalog_product_entity b on b.entity_id = a.entity_id
left join catalog_product_entity_varchar d on d.attribute_id = 85 and d.value = a.value
order by sku, is_default desc;
set #number = 0;
select
original_file_name,
sku,
is_default,
case
when
is_default = 0
then
#number := #number + 1
else #number := 0
end as number,
concat(sku, '_', if(is_default, 'default_1', concat('slideshow_', #number)), '.', file_extension) as new_file_name
from pictures;

Related

Select all orders with the specified status saved in another table (PDO)

At the moment i have all informations of a order in one table, including the order status.
In the future i will have a new table "status" to make a order history.
My tables currently look like this (simplified):
Table "orders":
id
date
name
10001
2021-08-24 16:47:52
Surname Lastname
10002
2021-08-30 17:32:05
Nicename Nicelastname
Table "status":
id
order_id
statusdate
status
1
10001
2021-08-24 16:47:52
new
2
10002
2021-08-30 17:32:05
new
3
10001
2021-08-26 13:44:11
pending
4
10001
2021-09-02 10:01:12
shipped
My problem is: At this moment i can select all orders with status "shipped" to list them like that:
$sql = $pdo->prepare("SELECT * FROM orders WHERE status = ?");
(? is my status, e.g. "shipped")
I know i have to use LEFT JOIN to combine the two tables (correct? or is there a better/easier way?), but i have absolutely no idea how i can select all orders with status X, because the "status" table can have multiple entries per order_id... So the statement must select only the newest entrie!?
Iy you have no 1 timestamps with the same date, you can use the first query the second is in case you can have multiple timestampo for the same order
CREATE TABLE orders
(`id` int, `date` varchar(19), `name` varchar(21))
;
INSERT INTO orders
(`id`, `date`, `name`)
VALUES
(10001, '2021-08-24 16:47:52', 'Surname Lastname'),
(10002, '2021-08-30 17:32:05', 'Nicename Nicelastname')
;
CREATE TABLE status
(`id` int, `order_id` int, `statusdate` varchar(19), `status` varchar(7))
;
INSERT INTO status
(`id`, `order_id`, `statusdate`, `status`)
VALUES
(1, 10001, '2021-08-24 16:47:52', 'new'),
(2, 10002, '2021-08-30 17:32:05', 'new'),
(3, 10001, '2021-08-26 13:44:11', 'pending'),
(4, 10001, '2021-09-02 10:01:12', 'shipped')
,
(5, 10001, '2021-09-02 10:01:13', 'shipped')
;
select o.`id` FROM orders o
| id |
| ----: |
| 10001 |
| 10002 |
select o.`id`,o.`date`, o.`name`,s.`statusdate`
from orders o join status s on o.`id` = s.order_id
where s.`status` = "shipped"
AND s.`statusdate` = (SELECT MAX(`statusdate`) FROM `status` WHERE order_id = o.`id` AND `status` = "shipped")
order by o.`id` desc
id | date | name | statusdate
----: | :------------------ | :--------------- | :------------------
10001 | 2021-08-24 16:47:52 | Surname Lastname | 2021-09-02 10:01:13
SELECT
id, `date`, `name`,`statusdate`
FROM
(SELECT `date`, `name`,`statusdate`,IF( `id` = #id,#rownum := #rownum +1,#rownum :=1) rn, #id := `id` as id
FROM (select o.`id`,o.`date`, o.`name`,s.`statusdate`
from orders o join status s on o.`id` = s.order_id
where s.`status` = "shipped"
order by o.`id` desc,s.`statusdate` DESC) t2 ,(SELECT #id := 0, #rownum:=0) t1) t2
WHERE rn = 1
id | date | name | statusdate
----: | :------------------ | :--------------- | :------------------
10001 | 2021-08-24 16:47:52 | Surname Lastname | 2021-09-02 10:01:13
db<>fiddle here

SQL get only earlier date in repetitive rows

I have an SQL table contains transactions like this
ID | FK | STATUS | DATE
1 | A | K1 | 2021-01-01
2 | A | K1 | 2021-01-15
3 | A | K2 | 2021-01-30
4 | A | K2 | 2021-02-03
5 | B | K1 | 2021-01-12
6 | C | K1 | 2021-03-30
7 | C | K3 | 2021-09-15
As we can see, some FK have some records with different STATUS and different DATE. From each FK I want to get the first/earlier transaction date with status K1.
Maybe something like this.
ID | FK | STATUS | DATE
1 | A | K1 | 2021-01-01
5 | B | K1 | 2021-01-12
6 | C | K1 | 2021-03-30
How can I get the result like that?
Note : I'm using MariaDB version 10.5.9-MariaDB
CREATE TABLE my_table (
`ID` INTEGER,
`FK` VARCHAR(1),
`STATUS` VARCHAR(2),
`DATE` DATETIME
);
INSERT INTO my_table
(`ID`, `FK`, `STATUS`, `DATE`)
VALUES
('1', 'A', 'K1', '2021-01-01'),
('2', 'A', 'K1', '2021-01-15'),
('3', 'A', 'K2', '2021-01-30'),
('4', 'A', 'K2', '2021-02-03'),
('5', 'B', 'K1', '2021-01-12'),
('6', 'C', 'K1', '2021-03-30'),
('7', 'C', 'K3', '2021-09-15');
Query #1
SELECT
ID,
FK,
STATUS,
DATE
FROM (
SELECT
*,
row_number() over (partition by FK,STATUS order by DATE ASC) rn from my_table
) t
where STATUS='K1' and rn=1;
ID
FK
STATUS
DATE
1
A
K1
2021-01-01 00:00:00
5
B
K1
2021-01-12 00:00:00
6
C
K1
2021-03-30 00:00:00
View on DB Fiddle
WITH cte_customers AS (
SELECT
ROW_NUMBER() OVER(PARTITION BY fk,status
ORDER BY
date
) row_num,
customer_id,
fk,
status
FROM
sales.customers
) SELECT
*
FROM
cte_customers
WHERE
row_num =1;
This is solved with a windowing function:
WITH RowNumbers AS (
SELECT *,
row_number() OVER (PARTITION BY FK ORDER BY Date) As rn
FROM [MyTable]
WHERE Status = 'K1'
)
SELECT ID, FK, STATUS, DATE
FROM RowNumbers
WHERE rn = 1
Window functions were added to MariaDB in version 10.2. The CTE is also needed because row_number() isn't computed until after the WHERE clause is processed.

How to enhance MySQL query to get categories and subcategories for a specific store

I am doing the following query to return the categories and subcategories for a specific store
SELECT c1.name, c2.name FROM categories c1
LEFT JOIN categories c2 ON c2.parent_id = c1.id
JOIN store s ON s.category_id = c1.id
WHERE c1.parent_id = 0
AND s.id = 1
Here is a look of how the tables are structured
categories
id | name | parent_id
1 | test1 | 0
2 | test2 | 1
3 | test3 | 0
4 | test4 | 1
store
id | category_id
1 | 1
1 | 2
1 | 3
I need the query to return something like
categories | subcategories
test 1 | test2
test 3 | NULL
Currently it's returning test 1 | test 4 as well although store.category_id = 4 does not exist. Can you please advise what am I doing wrong?
For that you need to select the category_id and use only categoires that are in store id
CREATE TABLE store (
`id` INTEGER,
`category_id` INTEGER
);
INSERT INTO store
(`id`, `category_id`)
VALUES
('1', '1'),
('1', '2'),
('1', '3');
CREATE TABLE categories (
`id` INTEGER,
`name` VARCHAR(5),
`parent_id` INTEGER
);
INSERT INTO categories
(`id`, `name`, `parent_id`)
VALUES
('1', 'test1', '0'),
('2', 'test2', '1'),
('3', 'test3', '0'),
('4', 'test4', '1');
SELECT c1.name, c2.name FROM (categories c1
LEFT JOIN categories c2 ON c2.parent_id = c1.id)
wHERE c1.parent_id = 0
AND (c2.id IN (SELECT `category_id` FROM store WHERE id = 1) OR c2.id iS NuLL)
name | name
:---- | :----
test1 | test2
test3 | null
db<>fiddle here

Mysql5.6 select statement with duplicate id and set

I have a table A containing id (auto incremented) as a primary key and a table B containing id as a foreign key.While selecting data from Table B if there are duplicate ids then append a number with empid.
TableA
id empid name place
1 ab123 John SL
2 gh345 Lucy AK
3 hj890 Mike KL
TableB
id class
1 A
1 B
1 A
2 A
3 A
Output
ab123,SL,A
ab123,SL,B
ab123,SL,A
gh345,AK,A
hj890,KL,A
Desired output
ab123-1,SL,A
ab123-2,SL,B
ab123-3,SL,A
gh345,AK,A
hj890,KL,A
This is what I have tried
SELECT TableA.empid, ",", TableA.place, ",", TableB.class
FROM TableA
INNER JOIN TableB ON TableA.id = TableB.id
With mysql 8 You can do following code, in mysql 6.x you need to use user-defined variables instead of the window finction
CREATE TABLE TableA (
`id` INTEGER,
`empid` VARCHAR(5),
`name` VARCHAR(4),
`place` VARCHAR(2)
);
INSERT INTO TableA
(`id`, `empid`, `name`, `place`)
VALUES
('1', 'ab123', 'John', 'SL'),
('2', 'gh345', 'Lucy', 'AK'),
('3', 'hj890', 'Mike', 'K');
CREATE TABLE TableB (
`id` INTEGER,
`class` VARCHAR(1)
);
INSERT INTO TableB
(`id`, `class`)
VALUES
('1', 'A'),
('1', 'B'),
('1', 'A'),
('2', 'A'),
('3', 'A');
SELECT CONCAT(`empid`,IF(co > 1,CONCAT('-',`rn`),''),',',`place`,',',`class`) AS 'empid,place,class'
FROM
(SELECT
empid,place,class, ROW_NUMBER() OVER (PARTITION BY empid ) rn
, countB.co
FROM TableA
INNER JOIN TableB ON TableA.id = TableB.id
INNER JOIN (SELECT id,COUNT(*) co FROM TableB GROUP BY id) countB ON TableB.id = countB.id) t1
| empid,place,class |
| :---------------- |
| ab123-1,SL,A |
| ab123-2,SL,B |
| ab123-3,SL,A |
| gh345,AK,A |
| hj890,K,A |
db<>fiddle here
MySQL 5.x Version
SELECT CONCAT(`empid`,IF(co > 1,CONCAT('-',`rn`),''),',',`place`,',',`class`) AS 'empid,place,class'
FROM
(SELECT
place,class, IF(#id = empid,#rn := #rn+1, #rn := 1) rn
,#id := empid as empid
,countB.co
FROM TableA
INNER JOIN TableB ON TableA.id = TableB.id
INNER JOIN (SELECT id,COUNT(*) co FROM TableB GROUP BY id) countB ON TableB.id = countB.id
CROSS JOIN (SELECT #id :=0,#rn:=0) t2
ORDER BY empid) t1
| empid,place,class |
| :---------------- |
| ab123-1,SL,A |
| ab123-2,SL,B |
| ab123-3,SL,A |
| gh345,AK,A |
| hj890,K,A |
db<>fiddle here

How to update in MySql columns

There are 5 columns X and A, B, C, D in table t. Columns A, B, C, D are varchar. and column X has to show us how many of the next row repeating characters. I need help to update column X.
Example :
|ID | X | A | B | C | D |
=========================
| 4 | 1 | 7 | J | 7 | Q |
| 3 | 2 | K | Q | 8 | 8 |
| 2 | 3 | 7 | 8 | 9 | J | next row X=3
| 1 | 0 | 7 | J | 8 | K | 0 default
ID-1 is the first and X is zero by default and from there begin calculations ID-2 and X=3 because we have ID-1 "7" "J" and "8" and the next row ID-2 have the combination "7 8 9 J" in ID-1 there "7 J 8 -" and X should be 3. Values ​​of X can be between 0 and 4. ID-3, X=2 "- - 8 8" because in ID-2 have the combination "7 8 9 J" and i have 8 - twice in "K Q 8 8".
Added : 08/06
http://sqlfiddle.com/#!2/4586e/1
CREATE TABLE tmp
(
id int,
alnum CHAR(1),
cnt int,
PRIMARY KEY (id, alnum)
);
INSERT INTO tmp (id, alnum, cnt) SELECT id, A, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, B, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, C, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, D, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
UPDATE tab INNER JOIN (
SELECT t1.id AS id, SUM(t2.cnt) AS sum
FROM tmp t1 INNER JOIN tmp t2 ON t1.id + 1 = t2.id
AND t1.alnum = t2.alnum
GROUP BY t1.id
) tmp3 ON tab.id = tmp3.id + 1
SET tab.X = tmp3.sum;
SELECT * FROM tab WHERE X > 4;
Original answer : 08/05
How about using tmp table which stores for each A, B, C, D columns into single rows. This makes us easy to calculate X value.
Below code assumes id is sequencial value. If not, please let me know there is another query for it.
CREATE TABLE tab
(
id INT,
X INT,
A CHAR(1),
B CHAR(1),
C CHAR(1),
D CHAR(1)
);
INSERT INTO tab VALUES (1, 0, '7', 'J', '8', 'K');
INSERT INTO tab VALUES (2, 0, '7', '8', '9', 'J');
INSERT INTO tab VALUES (3, 0, 'K', 'Q', '8', '8');
INSERT INTO tab VALUES (4, 0, '7', 'J', '7', 'Q');
CREATE TABLE tmp
(
id INT,
alnum CHAR(1)
);
INSERT INTO tmp SELECT id, A FROM tab;
INSERT INTO tmp SELECT id, B FROM tab;
INSERT INTO tmp SELECT id, C FROM tab;
INSERT INTO tmp SELECT id, D FROM tab;
UPDATE tab INNER JOIN (
SELECT t1.id AS id, COUNT(*) AS cnt
FROM tmp t1 INNER JOIN tmp t2 ON t1.id + 1 = t2.id
AND t1.alnum = t2.alnum
GROUP BY t1.id
) tmp3 ON tab.id = tmp3.id + 1
SET tab.X = tmp3.cnt;
mysql> SELECT * FROM tab ORDER BY id DESC;
+------+------+------+------+------+------+
| id | X | A | B | C | D |
+------+------+------+------+------+------+
| 4 | 1 | 7 | J | 7 | Q |
| 3 | 2 | K | Q | 8 | 8 |
| 2 | 3 | 7 | 8 | 9 | J |
| 1 | 0 | 7 | J | 8 | K |
+------+------+------+------+------+------+
4 rows in set (0.00 sec)
The X values in your example are from the "next" row, not the previous. Assuming you have an auto-incrementing id, you can calculate this information by looking at the next row. You can generate a query to do the calculation:
select ((case when t1.A in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.B in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.C in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.D in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end)
) as X, t1.*
from (select t.*, (select max(id) from table t2 where t2.id > t.id) as nextid
from table t
) t1 left outer join
t tnext
on tnext.id = t1.nextid;
Depending on the database you are using, this code can be simplified and expressed differently. Also, the specific update syntax might depend on the database.