Mysql5.6 select statement with duplicate id and set - mysql

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

Related

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

MySQL Update Table from Another Table Recursively

I have two tables, table1 and table2, where table1 is updated to fill in missing (null) values based on matching fields in table2 to create a more complete table1.
I have tried numerous queries such as
UPDATE table1 INNER JOIN table2...SET...
and
UPDATE table1 SET ... (SELECT)...
However my results are always incomplete. Note that I have a much larger dataset in both tables in terms of both columns and rows). I just used this as simpler example.
Rules:
1) The `keyword` from table2 looks for a match in `keyword` in table1 and must accept partial matches.
2) No values can be overwritten in table1 (update null values only)
3) The lookup order is per run_order in table2.
Specific example:
Table1:
|-----|-------------------------------|----------|-----|---------|-------|
|t1_id|keyword |category |month|age |skill |
|-----|-------------------------------|----------|-----|---------|-------|
| 1 |childrens-crafts-christmas |kids | | | |
| 2 |kids-costumes | | |tween | |
| 3 |diy-color-printable |printable | | |easy |
| 4 |toddler-halloween-costume-page | | | | |
|-----|-------------------------------|----------|-----|---------|-------|
Table2:
|-----|---------|---------|----------|-----|----------|-------|
|t2_id|run_order|keyword |category |month|age |skill |
|-----|---------|---------|----------|-----|----------|-------|
| 1 | 1 |children | | |4-11 yrs | |
| 2 | 2 |printable| | | |easy |
| 3 | 3 |costume |halloween | 10 |0-12 years| |
| 4 | 4 |toddler | | |1-3 years | |
| 5 | 5 |halloween|holiday | 10 | | |
|-----|---------|---------|----------|-----|----------|-------|
Result:
|-----|-------------------------------|----------|-----|---------|-------|
|t1_id|keyword |category |month|age |skill |
|-----|-------------------------------|----------|-----|---------|-------|
| 1 |childrens-crafts-christmas |kids | |4-11 yrs | |
| 2 |kids-costumes |halloween | 10 |tween | |
| 3 |diy-color-printable printable |printable | | |easy |
| 4 |toddler-halloween-costume-page |holiday | 10 |1-3 years| |
|-----|-------------------------------|----------|-----|---------|-------|
MySQL for schema and table data:
DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS table2;
CREATE TABLE `table1` (
`t1_id` INT NOT NULL AUTO_INCREMENT,
`keyword` VARCHAR(200) NULL,
`category` VARCHAR(45) NULL,
`month` VARCHAR(45) NULL,
`age` VARCHAR(45) NULL,
`skill` VARCHAR(45) NULL,
PRIMARY KEY (`t1_id`));
CREATE TABLE `table2` (
`t2_id` INT NOT NULL AUTO_INCREMENT,
`run_order` INT NULL,
`keyword` VARCHAR(200) NULL,
`category` VARCHAR(45) NULL,
`month` INT NULL,
`age` VARCHAR(45) NULL,
`skill` VARCHAR(45) NULL,
PRIMARY KEY (`t2_id`));
INSERT INTO `table1` (`keyword`, `category`) VALUES ('childrens-crafts-christmas', 'kids');
INSERT INTO `table1` (`keyword`, `age`) VALUES ('kids-costumes', 'tween');
INSERT INTO `table1` (`keyword`, `category`, `skill`) VALUES ('diy-color-printable', 'printable', 'easy');
INSERT INTO `table1` (`keyword`) VALUES ('toddler-halloween-costume-page');
INSERT INTO `table2` (`run_order`, `keyword`, `age`) VALUES (1, 'children', '4-11 yrs');
INSERT INTO `table2` (`run_order`, `keyword`, `skill`) VALUES (2, 'printable', 'easy');
INSERT INTO `table2` (`run_order`, `keyword`, `category`, `month`, `age`) VALUES (3, 'costume', 'halloween', 10, '0-12 years');
INSERT INTO `table2` (`run_order`, `keyword`, `age`) VALUES (4, 'toddler', '1-3 years');
INSERT INTO `table2` (`run_order`, `keyword`, `category`, `month`) VALUES (5, 'halloween', 'holiday', 10);
You want to update empty values in table1 with the value in the corresponding column of the first matching record in table2, run_order wise. A typical solution is to use a combination of correlated subqueries to find the matching records in table2 and keep only the one with lowest run_order.
Here is a query that will update null categories with this logic:
update table1 t1
set category = (
select category
from table2 t2
where t2.run_order = (
select min(t22.run_order)
from table2 t22
where
t1.keyword like concat('%', t22.keyword, '%')
and t22.category is not null
)
)
where t1.category is null
This assumes that run_order is unique in table2 (which seems relevant in your use case).
You can extend the logic for more columns with coalesce(). Here is the solution for columns category and month:
update table1 t1
set
category = coalesce(
t1.category,
(
select category
from table2 t2
where t2.run_order = (
select min(t22.run_order)
from table2 t22
where
t1.keyword like concat('%', t22.keyword, '%')
and t22.category is not null
)
)
),
month = coalesce(
t1.month,
(
select month
from table2 t2
where t2.run_order = (
select min(t22.run_order)
from table2 t22
where
t1.keyword like concat('%', t22.keyword, '%')
and t22.month is not null
)
)
)
where t1.category is null or t1.month is null
Demo on DB Fiddle
You can use a join between the 2 tables using a like expression and the if nul() function to ensure you don't overwrite non null values.
UPDATE table1 t1
INNER JOIN table2 t2 ON t1.keyword like concat("%",t2.keyword,"%")
SET
t1.category = ifnull(t1.category,t2.category),
t1.age = ifnull(t1.age,t2.age),
t1.skill = ifnull(t1.skill,t2.skill);

Insert ordinal number

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;

JOIN from tables evaluating results from one of them

I have three tables to join, one of them with one-to several values.
SQLFIDDLE
CREATE TABLE Table1 (`id` int, `name` varchar(3));
INSERT INTO Table1 (`id`, `name`)
VALUES (1, 'A'), (2, 'B'), (3, 'C');
CREATE TABLE Table2 (`id` int, `status` int, `date` varchar(9));
INSERT INTO Table2 (`id`, `status`, `date`)
VALUES (1, 1, '''.11..'''), (1, 2, '''.12..'''), (1, 3, '''.13..'''),
(2, 3, '''.23..'''), (3, 1, '''.31..'''), (3, 3, '''.33..''')
;
CREATE TABLE Table3 (`id` int, `value` int);
INSERT INTO Table3 (`id`, `value`)
VALUES (1, 34), (2, 22), (3, 17);
Query 1:
select * from table1
| id | name |
|----|------|
| 1 | A |
| 2 | B |
| 3 | C |
Query 2:
select * from table2;
| id | status | date |
|----|--------|---------|
| 1 | 1 | '.11..' |
| 1 | 2 | '.12..' |
| 1 | 3 | '.13..' |
| 2 | 3 | '.23..' |
| 3 | 1 | '.31..' |
| 3 | 3 | '.33..' |
Query 3:
select * from table3
| id | value |
|----|-------|
| 1 | 34 |
| 2 | 22 |
| 3 | 17 |
I need query that returns for each id:
TABLE1.name, TABLE2.status, TABLE2.date, TABLE3.value
with this condition:
If TABLE2.status =1 exists then return ONLY that line of TABLE2
Else if TABLE2.status =1 does not exists then look for status =2 and return ONLY that line of TABLE2
If no one of those values are present in TABLE2 then skip that id from results
EDIT: TABLE2 has an UNIQUE key for id,status so there can be only one id=1 status=1
Thanks for your help!
Something like this maybe:
select table1.id, table1.name,
coalesce(table2_status1.status, table2_status2.status) as status,
coalesce(table2_status1.date, table2_status2.date) as date,
table3.value
from table1
left join table2 table2_status1 on table2_status1.id = table1.id and table2_status1.status = 1
left join table2 table2_status2 on table2_status2.id = table1.id and table2_status2.status = 2
join table3 on table3.id = table1.id
where (table2_status1.id is not null or table2_status2.id is not null);
Not performant, using subselects, works ( but rlanvins https://stackoverflow.com/a/48235077/7505395 is nicer) :
A,B,C instead of First, Second, ...
select
TABLE1.name,
case
when exists( select 1 from table2 where id = table1.id and status = 1)
then 1
when exists( select 1 from table2 where id = table1.id and status = 2)
then 2
end as T2status,
case
when exists( select 1 from table2 where id = table1.id and status = 1)
then ( select date from table2 where id = table1.id and status = 1)
when exists( select 1 from table2 where id = table1.id and status = 2)
then ( select date from table2 where id = table1.id and status = 2)
end as T2date,
TABLE3.value
from table1
join table3 on table1.id = table3.id
where
exists( select 1 from table2 where id = table1.id and status = 1)
or exists( select 1 from table2 where id = table1.id and status = 2)
Output
Name T2status T2date value
A 1 '.11..' 34
C 1 '.31..' 17
DDL
CREATE TABLE Table1 (`id` int, `name` varchar(3));
INSERT INTO Table1 (`id`, `name`)
VALUES (1, 'A'), (2, 'B'), (3, 'C');
CREATE TABLE Table2 (`id` int, `status` int, `date` varchar(9));
INSERT INTO Table2 (`id`, `status`, `date`)
VALUES (1, 1, '''.11..'''), (1, 2, '''.12..'''), (1, 3, '''.13..'''),
(2, 3, '''.23..'''), (3, 1, '''.31..'''), (3, 3, '''.33..''')
;
CREATE TABLE Table3 (`id` int, `value` int);
INSERT INTO Table3 (`id`, `value`)
VALUES (1, 34), (2, 22), (3, 17);

can not get correct results with group by in mysql

I have 2 SQL tables
CREATE TABLE A(
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name CHAR(1) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE B(
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
A_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO A VALUES (1, 'A'), (2, 'B'), (3, 'C'), (4, 'A');
INSERT INTO B VALUES (1, 1), (2, 2), (3, 4), (4, 4);
The tables look this way:
select * from A;
+----+------+
| id | name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | A |
+----+------+
select * from B;
+----+------+
| id | A_id |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 4 |
| 4 | 4 |
+----+------+
Now I want to find out how many each of the elements from table A are there in table B. Using other words I want to see:
A = 3
B = 1
C = 0
I tried to do this with: SELECT name, count(*) FROM A, B WHERE A.id = A_id GROUP BY A.id;, but it returns something completely weird. Can someone help me?
Query
SELECT a.name,COUNT(b.A_id) as `count`
FROM A a
LEFT JOIN B b
ON a.id=b.A_id
GROUP BY a.name;
Fiddle Demo
You just need a left outer join to handle the condition where there are no A's in B:
SELECT A.Name, COUNT(b.id)
FROM A
LEFT OUTER JOIN B on A.id = B.a_id
GROUP BY A.Name;
SqlFiddle here
You should use a LEFT JOIN, and not GROUP BY A.id, but instead by name:
SELECT A.name, COUNT(B.A_id)
FROM A
LEFT JOIN B ON A.id = B.A_id
GROUP BY A.name;