Single query or stored procedure to fetch data - mysql

I have created three tables
Here I would like to fetch all question_tags and answers related to a perticular question(id).
for that needed Single query or stored procedure
create table questions(id varchar(100),title varchar(140),body varchar(2000),
primary key(id));
create table question_tags(id varchar(50),tag_name varchar(50),question_id varchar(100),
primary key(id),foreign key(question_id) references questions(id));
create table answers(id varchar(50),answer varchar(2000),question_id varchar(100),
primary key(id),foreign key(question_id) references questions(id));
and following data in table
mysql> select * from questions;
+----+-------+-------------+
| id | title | body |
+----+-------+-------------+
| 1 | a | hello |
| 2 | b | hii |
| 3 | c | bye |
| 4 | d | how are you |
+----+-------+-------------+
4 rows in set (0.03 sec)
mysql> select * from question_tags
+----+----------+-------------+
| id | tag_name | question_id |
+----+----------+-------------+
| t1 | java | 1 |
| t2 | mysql | 1 |
| t3 | jquery | 1 |
+----+----------+-------------+
3 rows in set (0.00 sec)
mysql> select * from answers;
+----+-----------+-------------+
| id | answer | question_id |
+----+-----------+-------------+
| a1 | good | 1 |
| a2 | excellent | 1 |
| a3 | ok | 1 |
+----+-----------+-------------+
3 rows in set (0.00 sec)

I think you search for the JOIN [ON] Statement. For your Tablelayout it should be something like this:
SELECT `question_tags`.*, `answers`.*
FROM `questions`
JOIN `answers`
ON `questions`.`id` = `answers`.`question_id`
JOIN `question_tags`
ON `questions`.`id` = `question_tags`.`question_id`
WHERE `questions`.`id` = {YOUR_ID}
Or only Question-Id, Answers, Tags:
SELECT `questions`.`id` AS `question_id`,
`question_tags`.`tag_name`,
`answers`.`answer`
FROM `questions`
JOIN `answers`
ON `questions`.`id` = `answers`.`question_id`
JOIN `question_tags`
ON `questions`.`id` = `question_tags`.`question_id`
WHERE `questions`.`id` = 0
EDIT:
And for everything in one Row:
SELECT `questions`.`id` AS `question_id` ,
(
SELECT GROUP_CONCAT( `question_tags`.`tag_name` SEPARATOR ';')
FROM `question_tags`
WHERE `question_id` =0
) AS `Tags` ,
(
SELECT GROUP_CONCAT( `answers`.`answer` SEPARATOR ';' )
FROM `answers`
WHERE `question_id` =0
) AS `Answers`
FROM `questions`
WHERE `questions`.`id` =0
Please keep in mind that this isn't the best way for doing this. I can't provide you a Stored Procedure since i'm unfamiliar with SP in MySQL.
In my Opinion there is no way to select/return/display your desired format as a Table.

The query is:
select * from questions join question_tags on (questions.id = question_tags.question_id) join answers on (questions.id=answers.question_id);
Here is a fiddle; it greatly helps us assit you if you provide one with your question.
Please read about mySQL naming conventions (tables in signular) and about keyreferences (I have changed yours to integers but you can also auto_increment etc).

Related

MySQL Delete with Join and Count

I have two tables s_filter_values and s_filter_articles.
s_filter_values: s_filter_articles:
| id | value | | articleID | valueID |
|----|-------| |-----------|---------|
| 1 | one | | 1 | 2 |
| 2 | two | | 1 | 3 |
| 3 | three | | 2 | 2 |
With the following statement I count the the occurence of the values
respectively I get the values that are not linked to an article:
SELECT v.*, IFNULL(COUNT(a.articleID), 0) AS counter
FROM s_filter_values AS v
LEFT JOIN s_filter_articles AS a ON v.id = a.valueID
GROUP BY v.id
HAVING counter = 0
In this case, I got
| id | value | counter |
| 1 | one | 0 |
My questions is: How can I use this statement to delete all rows from s_filter_values that are not linked to an article?
I will suggest using NOT EXIST rather than NOT IN.
The NOT EXISTS should perform faster on a large dataset.
There is one key difference between the two constructs: if the subquery returns a NULL in its results then the NOT IN condition will fail, because null is neither equal-to nor not-equal-to any other value.
create table s_filter_values(
id int(10),
`value` varchar(10) );
insert into s_filter_values values ( 1,'one'),( 2,'two'),( 3,'three');
create table s_filter_articles(
articleID int(10),
valueID int(10) );
insert into s_filter_articles values ( 1,2),( 1,3),( 2,2);
DELETE FROM s_filter_values
WHERE NOT EXISTS (SELECT valueID FROM s_filter_articles a where a.valueID= s_filter_values.id);
Demo: https://www.db-fiddle.com/f/7yUJcuMJPncBBnrExKbzYz/84
I would use a subquery to get all IDs that are in the table. Then drop the rows from s_filter_values that are not present.
DELETE FROM s_filter_values WHERE id NOT IN (SELECT DISTINCT valueID FROM s_filter_articles);
A simple sub select should do it:
DELETE FROM `s_filter_values`
WHERE `id` NOT IN SELECT DISTINCT `valueID` FROM `s_filter_articles`

MySQL Update a column with values combined from target column and another column

I want to update column user with the last 6 characters replaced by id padded with zero. All keys under column user are in the format ^[A-Z]{2}[0-9]{8}$. For example, here's some sample data:
MariaDB [test]> SELECT * FROM users ORDER BY id ASC LIMIT 3;
+----+------------+
| id | user |
+----+------------+
| 1 | AS12491264 |
| 2 | LQ10240183 |
| 3 | MR12037108 |
+----+------------+
3 rows in set (0.00 sec)
The expected result is:
+----+------------+
| id | user |
+----+------------+
| 1 | AS12000001 |
| 2 | LQ10000002 |
| 3 | MR12000003 |
+----+------------+
I worked out the following two queries but both fail:
UPDATE users AS u
SET u.user = (
SELECT
CONCAT(
SUBSTRING(s.user, 0, 4),
LPAD(s.id, 6, '0')
)
FROM users AS s
WHERE s.id = u.id
);
UPDATE users AS u
INNER JOIN (
SELECT
s.id AS id,
CONCAT(
SUBSTRING(s.user, 0, 4),
LPAD(s.id, 6, '0')
) AS data
FROM users AS s
) AS s ON s.id = u.id
SET u.user = s.data;
Both fail and give this result:
+----+--------+
| id | user |
+----+--------+
| 1 | 000001 |
| 2 | 000002 |
| 3 | 000003 |
+----+--------+
Here's the DB schema (fetched from mysqldump):
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user` (`user`)
) ENGINE=InnoDB AUTO_INCREMENT=8964 DEFAULT CHARSET=utf8;
I am running on MariaDB 10.1.38.
This should do what you want:
UPDATE users
SET user = CONCAT(LEFT(user,4), LPAD(id, 6, '0'))
Output:
id user
1 AS12000001
2 LQ10000002
3 MR12000003
Demo on dbfiddle
Update
Should the user column have a variable length, you can replace just the last 6 characters using this query, which uses CHAR_LENGTH to determine the length of the string and subtracts 6 from that to determine how many characters on the left to keep. We use CHAR_LENGTH instead of LENGTH so the query still works for multi-byte character encodings.
UPDATE users
SET user = CONCAT(LEFT(user,CHAR_LENGTH(user)-6), LPAD(id, 6, '0'))
Demo on dbfiddle

phpmyadmin update m to n relationship table to remove duplicates from separate table

I have 2 tables.
CREATE TABLE designs
( game_id INT NOT NULL,
des_id INT NOT NULL,
PRIMARY KEY(game_id, des_id),
FOREIGN KEY(game_id) REFERENCES Game(id),
ON UPDATE CASCADE)
CREATE TABLE designer
( name VARCHAR(30) NOT NULL,
id INT NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES designs(des_id),
ON UPDATE CASCADE);
Lets say I have data:
designs:
0---0
0---1
1---2
2---3
2---4
.............................
designer:
Bob---0
Jill---1
Bob---2
Rob---3
Jill---4
After the update, I would like the "designs" table to look like:
0---0
0---1
1---0
2---3
2---1
What update query would I need to accomplish this?
Some queries I tried are:
UPDATE designs
SET des_id = (
SELECT a.id
FROM designer as a
JOIN designer as b
ON a.name=b.name AND a.id < b.id
WHERE des_id = b.id);
...
UPDATE `designs` as a
JOIN designer as b
ON a.des_id=b.id
SET a.des_id = b.id
WHERE b.id = (
SELECT c.id
FROM designer as c
LEFT JOIN designer as d
ON c.name=d.name
WHERE c.id<d.id)
Here's one idea. Note that it uses an documented hack in the form of a 'group by/order by' trick:
UPDATE designs d
JOIN
( select d1.id matcher_id
, d2.id select_id
from `designer` d1
JOIN designer d2
ON d1.name = d2.name
group
by d1.id
Order
by d2.id
) x
ON x.matcher_id = d.des_id
SET d.des_id = select_id
Your LEFT JOIN idea is almost right, but here's another idea which is faster...
DROP TABLE IF EXISTS designs;
CREATE TABLE designs
( game_id INT NOT NULL
, designer_id INT NOT NULL
, PRIMARY KEY(game_id, designer_id)
);
DROP TABLE IF EXISTS designers;
CREATE TABLE designers
( name VARCHAR(30) NOT NULL
, designer_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
INSERT INTO designs VALUES
(1,1),
(1,2),
(2,3),
(3,4),
(3,5);
INSERT INTO designers VALUES
('Bob',1),
('Jill',2),
('Bob',3),
('Rob',4),
('Jill',5);
SELECT * FROM designs;
+---------+-------------+
| game_id | designer_id |
+---------+-------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 3 | 5 |
+---------+-------------+
SELECT * FROM designers;
+------+-------------+
| name | designer_id |
+------+-------------+
| Bob | 1 |
| Jill | 2 |
| Bob | 3 |
| Rob | 4 |
| Jill | 5 |
+------+-------------+
UPDATE designs g
JOIN designers d
ON d.designer_id = g.designer_id
JOIN designers x ON x.name = d.name
JOIN
( SELECT name
, MIN(designer_id) min_designer_id
FROM designers
GROUP
BY name
) y
ON y.name = x.name
AND y.min_designer_id = x.designer_id
SET g.designer_id = x.designer_id;
SELECT * FROM designs;
+---------+-------------+
| game_id | designer_id |
+---------+-------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 2 |
| 3 | 4 |
+---------+-------------+
Actually, in the special case of an UPDATE, I think this will work just as well, and I'm not really sure that it's any less performative...
UPDATE designs g
JOIN designers x
ON x.designer_id = g.designer_id
JOIN designers y
ON y.name = x.name
AND y.designer_id < x.designer_id
SET g.designer_id = y.designer_id;

Update mySQL field with variable number of other fields

i have a mySQL table set up like this
+----+----------+---------+
| id | parentid | content |
+----+----------+---------+
| 1 | 0 | a |
| 2 | 1 | b |
| 3 | 0 | c |
| 4 | 3 | d |
| 5 | 3 | e |
| 6 | 3 | f |
+----+----------+---------+
what i would like to do is concatenate the content of the children onto the end of the parent (then delete the children, but i will do that later), in ASC order based on id. so the result should look like this (without children)
+----+----------+---------+
| id | parentid | content |
+----+----------+---------+
| 1 | 0 | ab |
| 3 | 0 | cdef |
+----+----------+---------+
the issue im running into is that as you can see a parent may have more than one child. so far the query i have is
UPDATE table
SET content = CONCAT(content,
...
) ORDER BY id ASC
im not sure what to place in the ... section to grab all of the children and append them in order they were retrieved. maybe im going about it the wrong way. any help will be greatly appreciated
One option is:
/*Table structure for table `table` */
DROP TABLE IF EXISTS `table`;
CREATE TABLE `table` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`parentid` INT(11) UNSIGNED NOT NULL,
`content` VARCHAR(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB;
/*Data for the table `table` */
INSERT INTO `table`(`parentid`,`content`)
VALUES
(0,'a'),(1,'b'),(0,'c'),(3,'d'),(3,'e'),(3,'f');
UPDATE `table`
INNER JOIN
(SELECT `t0`.`id`, CONCAT(`t0`.`content`, GROUP_CONCAT(`t1`.`content` SEPARATOR '')) AS `content`
FROM `table` `t0`
INNER JOIN `table` `t1` ON `t0`.`id` = `t1`.`parentid`
WHERE `t0`.`parentid` = 0
GROUP BY `t1`.`parentid`) `der`
SET `table`.`content` = `der`.`content`
WHERE `table`.`id` = `der`.`id`;
DELETE FROM `table` WHERE `parentid` > 0;
SQL Fiddle demo
You have to perfom so much changes to the table that it might be better to create another one with the new data and drop the current one.
The query that will get you the results you're looking for is:
SELECT
min(t1.id) id,
0 parentid,
group_concat(t1.content ORDER BY t1.id separator '') content
FROM t t1
LEFT JOIN t t2 ON t1.parentid = t2.id
GROUP BY coalesce(t2.id, t1.id);
Fiddle here.

How to rewrite a NOT IN subquery as join

Let's assume that the following tables in MySQL describe documents contained in folders.
mysql> select * from folder;
+----+----------------+
| ID | PATH |
+----+----------------+
| 1 | matches/1 |
| 2 | matches/2 |
| 3 | shared/3 |
| 4 | no/match/4 |
| 5 | unreferenced/5 |
+----+----------------+
mysql> select * from DOC;
+----+------+------------+
| ID | F_ID | DATE |
+----+------+------------+
| 1 | 1 | 2000-01-01 |
| 2 | 2 | 2000-01-02 |
| 3 | 2 | 2000-01-03 |
| 4 | 3 | 2000-01-04 |
| 5 | 3 | 2000-01-05 |
| 6 | 3 | 2000-01-06 |
| 7 | 4 | 2000-01-07 |
| 8 | 4 | 2000-01-08 |
| 9 | 4 | 2000-01-09 |
| 10 | 4 | 2000-01-10 |
+----+------+------------+
The columns ID are the primary keys and the column F_ID of table DOC is a not-null foreign key that references the primary key of table FOLDER. By using the 'DATE' of documents in the where clause, I would like to find which folders contain only the selected documents. For documents earlier than 2000-01-05, this could be written as:
SELECT DISTINCT d1.F_ID
FROM DOC d1
WHERE d1.DATE < '2000-01-05'
AND d1.F_ID NOT IN (
SELECT d2.F_ID
FROM DOC d2 WHERE NOT (d2.DATE < '2000-01-05')
);
and it correctly returns '1' and '2'. By reading
http://dev.mysql.com/doc/refman/5.5/en/rewriting-subqueries.html
the performance for big tables could be improved if the subquery is replaced with a join. I already found questions related to NOT IN and JOINS but not exactly what I was looking for. So, any ideas of how this could be written with joins ?
The general answer is:
select t.*
from t
where t.id not in (select id from s)
Can be rewritten as:
select t.*
from t left outer join
(select distinct id from s) s
on t.id = s.id
where s.id is null
I think you can apply this to your situation.
select distinct d1.F_ID
from DOC d1
left outer join (
select F_ID
from DOC
where date >= '2000-01-05'
) d2 on d1.F_ID = d2.F_ID
where d1.date < '2000-01-05'
and d2.F_ID is null
If I understand your question correctly, that you want to find the F_IDs representing folders which only contains documents from before '2000-01-05', then simply
SELECT F_ID
FROM DOC
GROUP BY F_ID
HAVING MAX(DATE) < '2000-01-05'
Sample Table and Insert Statements
CREATE TABLE `tleft` (
`id` int(2) NOT NULL,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `tright` (
`id` int(2) NOT NULL,
`t_left_id` int(2) DEFAULT NULL,
`description` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT INTO `tleft` (`id`, `name`)
VALUES
(1, 'henry'),
(2, 'steve'),
(3, 'jeff'),
(4, 'richards'),
(5, 'elon');
INSERT INTO `tright` (`id`, `t_left_id`, `description`)
VALUES
(1, 1, 'sample'),
(2, 2, 'sample');
Left Join : SELECT l.id,l.name FROM tleft l LEFT JOIN tright r ON l.id = r.t_left_id ;
Returns Id : 1, 2, 3, 4, 5
Right Join : SELECT l.id,l.name FROM tleft l RIGHT JOIN tright r ON l.id = r.t_left_id ;
Returns Id : 1,2
Subquery Not in tright : select id from tleft where id not in ( select t_left_id from tright);
Returns Id : 3,4,5
Equivalent Join For above subquery :
SELECT l.id,l.name FROM tleft l LEFT JOIN tright r ON l.id = r.t_left_id WHERE r.t_left_id IS NULL;
AND clause will be applied during the JOIN and WHERE clause will be applied after the JOIN .
Example : SELECT l.id,l.name FROM tleft l LEFT JOIN tright r ON l.id = r.t_left_id AND r.description ='hello' WHERE r.t_left_id IS NULL ;
Hope this helps