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.
Related
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`
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).
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
I got this table "A":
| id | date |
===================
| 1 | 2010-01-13 |
| 2 | 2011-04-19 |
| 3 | 2011-05-07 |
| .. | ... |
and this table "B":
| date | value |
======================
| 2009-03-29 | 0.5 |
| 2010-01-30 | 0.55 |
| 2011-08-12 | 0.67 |
Now I am looking for a way to JOIN those two tables having the "value" column in "B" mapped to the dates in "A". The tricky part for me here is that table "B" only stores the change date and the new value. Now when I need this value in table "A" the SQL needs to look back what date is the next below the date it is asking the value for.
So in the end the JOIN of those tables should look like this:
| id | date | value |
===========================
| 1 | 2010-01-13 | 0.5 |
| 2 | 2011-04-19 | 0.55 |
| 3 | 2011-05-07 | 0.55 |
| .. | ... | ... |
How can I do this?
-- Create and fill first table
CREATE TABLE `id_date` (
`id` int(11) NOT NULL auto_increment,
`iddate` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
INSERT INTO `id_date` VALUES(1, '2010-01-13');
INSERT INTO `id_date` VALUES(2, '2011-04-19');
INSERT INTO `id_date` VALUES(3, '2011-05-07');
-- Create and fill second table
CREATE TABLE `date_val` (
`mydate` date NOT NULL,
`myval` varchar(4) collate utf8_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
INSERT INTO `date_val` VALUES('2009-03-29', '0.5');
INSERT INTO `date_val` VALUES('2010-01-30', '0.55');
INSERT INTO `date_val` VALUES('2011-08-12', '0.67');
-- Get the result table as asked in question
SELECT iddate, t2.mydate, t2.myval
FROM `id_date` t1
JOIN date_val t2 ON t2.mydate <= t1.iddate
AND t2.mydate = (
SELECT MAX( t3.mydate )
FROM `date_val` t3
WHERE t3.mydate <= t1.iddate )
What we're doing:
for each date in the id_date table (your table A),
we find the date in the date_val table (your table B)
which is the highest date in the date_val table (but still smaller than the id_date.date)
You could use a subquery with limit 1 to look up the latest value in table B:
select id
, date
, (
select value
from B
where B.date < A.date
order by
B.date desc
limit 1
) as value
from A
I have been inspired by the other answers but ended with my own solution using common table expressions:
WITH datecombination (id, adate, bdate) AS
(
SELECT id, A.date, MAX(B.Date) as Bdate
FROM tableA A
LEFT JOIN tableB B
ON B.date <= A.date
GROUP BY A.id, A.date
)
SELECT DC.id, DC.adate, B.value FROM datecombination DC
LEFT JOIN tableB B
ON DC.bdate = B.bdate
The INNER JOIN return rows when there is at least one match in both tables. Try this.
Select A.id,A.date,b.value
from A inner join B
on A.date=b.date
I have three tables I'd like to join in a way that produces all records from one table and any matching records or NULL from another table. It is imperative that all records from the first table be returned. I thought I had done this before but I can't remember when or where and MySQL just isn't playing ball.
SELECT VERSION();
5.0.51a-3ubuntu5.7
/* Some table that may or may not be needed, included to give context */
CREATE TABLE `t1` (
`a` int(4) NOT NULL,
`a_name` varchar(10) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `t2` ( /* "One table" */
`b` int(2) NOT NULL,
`b_name` varchar(10) NOT NULL,
PRIMARY KEY (`b`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `t3` ( /* "Another table" */
`a` int(4) NOT NULL,
`b` int(2) NOT NULL,
PRIMARY KEY (`a`,`b`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO t1 VALUES (1, '1-one'),(2, '1-two'),(3, '1-three');
INSERT INTO t2 VALUES (1, '2-one'),(2, '2-two'),(3, '2-three'),
(4, '2-four'),(5, '2-five');
INSERT INTO t3 VALUES (1,1),(1,2),(1,3),(1,4),(2,2),(2,5);
t3 is a junction table for t1 and t2. The result set I'm looking for should look like this for any a=n:
n=1
b | b_name | a
-------------------
1 | 2-one | 1
2 | 2-two | 1
3 | 2-three | 1
4 | 2-four | 1
5 | 2-five | NULL
n=2
b | b_name | a
-------------------
1 | 2-one | NULL
2 | 2-two | 2
3 | 2-three | NULL
4 | 2-four | NULL
5 | 2-five | 2
n=7
b | b_name | a
-------------------
1 | 2-one | NULL
2 | 2-two | NULL
3 | 2-three | NULL
4 | 2-four | NULL
5 | 2-five | NULL
The value of a in the result set actually isn't important as long as it unambiguously reflects the presence or absence of records in t3 (does that make sense?).
The query
SELECT t2.b, t2.b_name, a
FROM t2
LEFT /* OUTER */ JOIN t3 ON t3.b = t2.b
WHERE (
a = 2
OR
a IS NULL
);
returns
b | b_name | a
-------------------
2 | 2-two | 2
5 | 2-five | 2
Can this be done?
SELECT t2.b, t2.b_name, MAX(IF(a=2, a, NULL)) AS a
FROM t2
LEFT OUTER JOIN t3
ON t3.b = t2.b
GROUP by t2.b
ORDER BY b ASC;
Something like this? If not, can you give an example of the output you'd like to get.
select * from t1
left join t3 using(a)
left join t2 using(b)