how can i select row's index from multi select - mysql

how can I Derive the query..?
Create Table Query:
CREATE TABLE `account_list` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nick` char(12) NOT NULL DEFAULT '',
`sponsor` char(12) DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `nick` (`nick`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
Example table data:
ID NICK SPONSOR
1 A NULL
2 B C
3 C NULL
4 D C
SELECT a.id, a.nick,b.sponsor
FROM account_list a (select b.sponsor from account_list b)
then, I wanted some like this:
SELECT id, nick, sponsor FROM `account_list` ....
ID NICK SPONSOR
1 A
2 B 3
3 C
4 D 3
look the sponsor row.
printed C is 3.
how can I select look like this?

Your query was almost right(I think) the sub query where clause should reference nick
drop table if exists t;
create table t
(ID int, NICK varchar(1), SPONSOR varchar(1));
insert into t values
(1 , 'A' , NULL),
(2 , 'B' , 'C'),
(3 , 'C' , NULL),
(4 , 'D' , 'C');
SELECT id, nick,sponsor,
(select b.id
from t as b
WHERE a.sponsor= b.nick and a.sponsor is not null
)
FROM t as a
;
+------+------+---------+--------------------------------------------------------------------------------------------------+
| 1 | A | NULL | NULL |
| 2 | B | C | 3 |
| 3 | C | NULL | NULL |
| 4 | D | C | 3 |
+------+------+---------+--------------------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

You could try using account_list for left join
SELECT a.id, a.nick, b.id sponsor
from account_list a
left join account_list b ON b.nick = a.sponsor

Related

Change json field in mysql to include data from other table

I have these two tables:
CREATE TABLE `config_support_departments` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `support_tickets_filters` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`filter_departments` json DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
config_support_departments:
+----+-------------------------+
| id | title |
+----+-------------------------+
| 1 | Projects / File Support |
| 2 | Sales Support |
| 3 | IT Support |
+----+-------------------------+
support_tickets_filters:
+----+--------------------+
| id | filter_departments |
+----+--------------------+
| 1 | ["2", "3"] |
+----+--------------------+
What I need is when query the support_tickets_filters table to also include the title from the config_support_departments table. Hence, the result should be something like this:
+----+-----------------------------------------------+
| id | filter_departments |
+----+-----------------------------------------------+
| 1 | {"2":"Sales Support","3":"IT Support"} |
+----+-----------------------------------------------+
You can use JSON_OBJECTAGG() function along with JSON_TABLE() if DB version is 8.0+
SELECT s.id, JSON_OBJECTAGG(c.id,c.title) AS filter_departments
FROM `support_tickets_filters` AS s
JOIN JSON_TABLE(
s.`filter_departments`,
'$[*]' COLUMNS (id INT PATH '$')
) j
JOIN `config_support_departments` AS c
ON j.id = c.id
GROUP BY s.id
For DB version 5.7, you can use one of the DB metadata tables such as information_schema.tables in order to generate index values as 0,1,... upto the length of the array (filter_departments) for extracting the related value from that iteratively
SELECT s.id, JSON_OBJECTAGG(c.id,c.title) AS filter_departments
FROM
(
SELECT #i := #i + 1 AS n, s.id,
JSON_UNQUOTE(JSON_EXTRACT(`filter_departments`,CONCAT('$[',#i,']'))) AS elm
FROM `support_tickets_filters` AS s
JOIN (SELECT #i := -1) AS iter
LEFT JOIN information_schema.tables AS t
ON #i < JSON_LENGTH(`filter_departments`) - 1 ) AS s
JOIN `config_support_departments` AS c
ON s.elm = c.id
GROUP BY s.id
Demo

MySql: how to get the desired result

I've a table like this:
CREATE TABLE `base_build_floor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`build_no` varchar(64) NOT NULL,
`build_name` varchar(64) DEFAULT NULL,
`floor_no` varchar(64) DEFAULT NULL,
`floor_name` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
)
and insert some data:
INSERT INTO `base_build_floor` VALUES ('41', 'BUILD40210011', 'A', null, null);
INSERT INTO `base_build_floor` VALUES ('42', 'BUILD40210012', 'B', null, null);
INSERT INTO `base_build_floor` VALUES ('43', 'BUILD40210013', 'C', null, null);
INSERT INTO `base_build_floor` VALUES ('44', 'BUILD40210013', 'C', 'FLOOR40210002', 'C1');
INSERT INTO `base_build_floor` VALUES ('45', 'BUILD40210013', 'C', 'FLOOR40210003', 'C2');
INSERT INTO `base_build_floor` VALUES ('46', 'BUILD40210012', 'B', 'FLOOR40210004', 'B1');
the table is about a build-floor table, first you should make a building, then, a building can has no or some floors. the A building has no floor, the B building has one floor named B1, the C building has two floors named C1 and C2, I want to get the result as below:
41 BUILD40210011 A null null
44 BUILD40210013 C FLOOR40210002 C1
45 BUILD40210013 C FLOOR40210003 C2
46 BUILD40210012 B FLOOR40210004 B1
it means that, if a building has no floors, then get it, while if a building has any one floor, the building itself should not be got, so how to write the mysql?I've tried to use Subquery but doesn't work
I've try like this :
SELECT
b.*
FROM
base_build_floor b
WHERE
b.floor_no IS NOT NULL
OR (
b.floor_no IS NULL
AND b.build_no NOT IN (
SELECT
GROUP_CONCAT(nostr)
FROM
(
SELECT
concat("'", f.build_no, "'") as nostr
FROM
base_build_floor f
WHERE
f.floor_no IS NOT NULL
GROUP BY
f.build_no
) t
)
)
but I get all the data
With NOT EXISTS:
select t.* from base_build_floor t
where t.floor_no is not null
or not exists (
select 1 from base_build_floor
where build_no = t.build_no and floor_no is not null
)
See the demo.
Results:
| id | build_no | build_name | floor_no | floor_name |
| --- | ------------- | ---------- | ------------- | ---------- |
| 41 | BUILD40210011 | A | | |
| 44 | BUILD40210013 | C | FLOOR40210002 | C1 |
| 45 | BUILD40210013 | C | FLOOR40210003 | C2 |
| 46 | BUILD40210012 | B | FLOOR40210004 | B1 |
This query would be much simpler if you had normalized tables. Ideally, you would have a buildings table with building id, no, and name, and a floors table with building id, floor no, and floor name. Then you could just join the two tables. Since that's not the case, we can basically extract the building and floor sub-tables from the main one and join them like this:
SELECT
b.build_no,
b.build_name,
f.floor_no,
f.floor_name
FROM
(SELECT DISTINCT build_no, build_name
FROM base_build_floor) b
LEFT OUTER JOIN
(SELECT *
FROM base_build_floor
WHERE floor_no IS NOT NULL) f ON b.build_no = f.build_no

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;

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

MySQL join, empty rows in junction table

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)