Struggling writing a nested query - mysql

I've got the following tables:
Products:
+---------------------------+
|id|name |details |price|cn|
|__|_____|_________|_____|__|
| 1|pen |somethi |100 |10|
| 2|paper|something|30 |11|
+---------------------------+
Categories:
+----------------------------+
|id | name |parent_id |
|____________________________|
|1 | granny | 0 |
|2 | dad | 1 |
|3 | grandson | 2 |
|4 | grandson 2| 2 |
+----------------------------+
Products2categories:
+-------------------------------+
| id | product_id | category_id|
|_______________________________|
| 1 | 1 | 3 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
+-------------------------------+
I want to do a query that will return all of the categories that some product is related to.
for example:
When I supply a product ID of 1 I would like to get as a result the result
grandson,dad,granny (name of the categories that this product is related to)
This is my attempt:
SELECT `categories`.`name`
FROM `categories`
JOIN (
SELECT `products2categories`.`category_id`,`products2categories`.`product_id`
FROM `products2categories` a
JOIN `products`
ON `products`.`id` = `products2categories`.`product_id`
)
ON `categories`.`id` = `products2categories`.`category_id`
I've got the following error:
Every derived table must have its own alias
I would like to get some help here :)
Thanks in advance!

Unless I'm misunderstanding your statement, I believe you're overcomplicating things. Based on your verbal description of what you want (and ignoring your code), this will do what you're asking:
SELECT name FROM categories WHERE id IN (SELECT id FROM products2categories WHERE product_id = 1);
EDIT: HERE IS A COMPLETE TEST
CREATE TABLE `categories` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `categories` VALUES (1,'granny',0),(2,'dad',1),(3,'grandson',2),(4,'grandson 2',2);
CREATE TABLE `products` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`details` varchar(255) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`cn` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products` VALUES (1,'pen','somethi',100,10),(2,'paper','something',30,11);
CREATE TABLE `products2categories` (
`id` int(11) DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products2categories` VALUES (1,1,3),(2,1,2),(3,1,1);
mysql> SELECT name FROM categories WHERE id IN (SELECT id FROM products2categories WHERE product_id = 1);
+----------+
| name |
+----------+
| granny |
| dad |
| grandson |
+----------+
3 rows in set (0.00 sec)

Well, I've just found out that I was doing it way too hard that it actually is.
For who-ever will come across it somewhen in the future, here is the solution:
SELECT `categories`.`name`
FROM `categories`
JOIN `products2categories`
ON `categories`.`id` = `products2categories`.`category_id`
WHERE `products2categories`.`product_id` = 1

Related

SQL/MySQL - Update target table with most recent entry

I'm looking for some help with a SQL/MySQL problem.
I have three source tables:
CREATE TABLE `customers` (
`cid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`customer_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
CREATE TABLE `standards` (
`sid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`standard_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
CREATE TABLE `partial_standard_compliance` (
`customer` bigint(20) unsigned NOT NULL,
`standard` bigint(20) unsigned NOT NULL,
`standard_compliance` bigint(20) unsigned DEFAULT NULL,
`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8
The idea is a customer gives themselves a rating using the standard_compliance column in the partial_standard_compliance table.
Customers can rate the same standard multiple times.
Result example:
+----------+----------+---------------------+---------------------+
| customer | standard | standard_compliance | created_time |
+----------+----------+---------------------+---------------------+
| 1 | 1 | 50 | 2023-01-28 16:19:34 |
| 1 | 1 | 60 | 2023-01-28 16:19:40 |
| 1 | 1 | 70 | 2023-01-28 16:19:48 |
| 2 | 10 | 30 | 2023-01-28 16:58:21 |
| 2 | 8 | 60 | 2023-01-28 16:58:32 |
| 2 | 9 | 60 | 2023-01-28 16:58:39 |
| 2 | 9 | 80 | 2023-01-28 16:58:43 |
+----------+----------+---------------------+---------------------+
I need to create a 4th table that has customer name, standard name and the most recent rating they have given themselves.
I have been trying with JOINS and CREATE AS SELECT, but haven't been able to solve it.
Any point in the right direction would be great. Thanks.
I have been trying with JOINS and CREATE AS SELECT
I need to create a 4th table that has customer name, standard name and
the most recent rating they have given themselves
Would be better if you create a view instead.
create view fourth_table as
select customer_name ,
standard_name ,
standard_compliance,
created_time
from (select c.customer_name,
s.standard_name,
psc.standard_compliance,
psc.created_time,
row_number() over(partition by c.customer_name order by psc.created_time desc ) as rn
from customers c
inner join partial_standard_compliance psc on psc.customer=c.cid
inner join standards s on s.sid=psc.standard
) x
where rn=1;
https://dbfiddle.uk/ZiK-k8jN
MySQL View

Filtering mysql query results by column priority

I am trying to filter out rows from joined select query results that have the same 'id_user' column value but are of lower sorting priority.
Column 'sort1' has the highest priority and 'sort4' has the lowest priority.The name of the column is what sets the priority not the value inside.
Table users:
CREATE TABLE `users` (
`user_id` int(11) NOT NULL,
`name` varchar(30) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `users` (`user_id`, `name`) VALUES
(1, 'Some Name'),
(2, 'Some Other Name');
ALTER TABLE `users`
ADD PRIMARY KEY (`user_id`);
Table items:
CREATE TABLE `items` (
`id_user` int(11) NOT NULL,
`sort1` int(11) DEFAULT NULL,
`sort2` int(11) DEFAULT NULL,
`sort3` int(11) DEFAULT NULL,
`sort4` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `items` (`id_user`, `sort1`, `sort2`, `sort3`, `sort4`) VALUES
(1, NULL, NULL, 39, NULL),
(2, NULL, NULL, NULL, 45),
(1, NULL, 34, NULL, NULL);
I tried the following query and get more results than i needed:
SELECT
users.name,
items.id_user,
items.sort1,
items.sort2,
items.sort3,
items.sort4
FROM
items
LEFT JOIN users ON items.id_user = users.user_id
WHERE
(
items.sort1 = '' OR items.sort1 IS NULL
) AND(
items.sort2 = 34 OR items.sort2 IS NULL
) AND(
items.sort3 = 39 OR items.sort3 IS NULL
) AND(
items.sort4 = 45 OR items.sort4 IS NULL
)
Actual result:
| Name | id_user | sort1 | sort2 | sort3 | sort4 |
+-----------------+---------+-------+-------+-------+-------|
| Some Name | 1 | NULL | NULL | 39 | NULL |
| Some Other Name | 2 | NULL | NULL | NULL | 45 |
| Some Name | 1 | NULL | 34 | NULL | NULL |
The first row is extra result because the third row has higher priority(sort2).
Expected result:
| Name | id_user | sort1 | sort2 | sort3 | sort4 |
+-----------------+---------+-------+-------+-------+-------|
| Some Name | 1 | NULL | 34 | NULL | NULL |
| Some Other Name | 2 | NULL | NULL | NULL | 45 |
I also tried GROUP BY id_user and ORDER BY priority columns but i didn't get the expected result
I changed your items table, added the id field
CREATE TABLE `items` (
`id` int(11) NOT NULL,
`id_user` int(11) NOT NULL,
`sort1` int(11) DEFAULT NULL,
`sort2` int(11) DEFAULT NULL,
`sort3` int(11) DEFAULT NULL,
`sort4` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `items` ADD PRIMARY KEY (`id`);
INSERT INTO `items` (`id`, `id_user`, `sort1`, `sort2`, `sort3`, `sort4`) VALUES
(1, 1, NULL, NULL, 39, NULL),
(2, 2, NULL, NULL, NULL, 45),
(3, 1, NULL, 34, NULL, NULL);
So you can try request like this, I changed FROM clause from items to users
SELECT
users.name,
items.id,
items.id_user,
items.sort1,
items.sort2,
items.sort3,
items.sort4
FROM
users
LEFT JOIN
items ON items.id = (
SELECT i.id FROM items AS i WHERE i.id_user = users.user_id ORDER BY -i.sort1 DESC, -i.sort2 DESC, -i.sort3 DESC, -i.sort4 DESC LIMIT 1
)
ORDER BY -items.sort1 DESC, -items.sort2 DESC, -items.sort3 DESC, -items.sort4 DESC
Result
| name | id | id_user | sort1 | sort2 | sort3 | sort4 |
|-----------------|----|---------|--------|--------|--------|--------|
| Some Name | 3 | 1 | (null) | 34 | (null) | (null) |
| Some Other Name | 2 | 2 | (null) | (null) | (null) | 45 |

Find the gaps for given date range in mysql, having multiple joins

I have four tables:
CREATE TABLE `A` (
`AID` bigint(20) NOT NULL AUTO_INCREMENT,
`Name` varchar(150) DEFAULT NULL,
PRIMARY KEY (`AID`)
);
CREATE TABLE `B` (
`BID` bigint(20) NOT NULL AUTO_INCREMENT,
`DtStart` datetime DEFAULT NULL,
`DtStop` datetime DEFAULT NULL,
`AID` bigint(20) DEFAULT NULL,
`CID` bigint(20) DEFAULT NULL,
PRIMARY KEY (`BID`)
);
CREATE TABLE `C` (
`CID` bigint(20) NOT NULL AUTO_INCREMENT,
`FLAGS` smallint(6) DEFAULT NULL,
PRIMARY KEY (`CID`)
);
CREATE TABLE `D` (
`DID` bigint(20) NOT NULL AUTO_INCREMENT,
`CID` bigint(20) DEFAULT NULL,
`Name` varchar(150) DEFAULT NULL,
PRIMARY KEY (`DID`)
);
I'm feeding data like this,
INSERT INTO A (Name) VALUES ("First");
INSERT INTO C (FLAGS) VALUES (1);
INSERT INTO B (DtStart, DtStop, AID, CID) VALUES ("2016-09-07", "2017-09-07", 1, 1);
INSERT INTO D (CID, Name) VALUES (1, "Alan");
INSERT INTO C (FLAGS) VALUES (2);
INSERT INTO B (DtStart, DtStop, AID, CID) VALUES ("2016-09-15", "2017-09-23", 1, 2);
INSERT INTO D (CID, Name) VALUES (2, "John");
When I hit the query:
SELECT
A.Name as Object, B.DtStart, B.DtStop, C.FLAGS, D.Name as User
FROM A
LEFT JOIN B ON B.AID=A.AID
LEFT JOIN C ON C.CID=B.CID
LEFT JOIN D ON D.CID=C.CID
WHERE "2017-09-01" <= B.DtStop AND "2017-10-01" > B.DtStart;
I get the result:
+--------+---------------------+---------------------+-------+------+
| Object | DtStart | DtStop | FLAGS | User |
+--------+---------------------+---------------------+-------+------+
| First | 2016-09-07 00:00:00 | 2017-09-07 00:00:00 | 1 | Alan |
| First | 2016-09-15 00:00:00 | 2017-09-23 00:00:00 | 2 | John |
+--------+---------------------+---------------------+-------+------+
2 rows in set (0.00 sec)
How could I find all the rows which also include gaps for the given date range?
In my example, I'm looking for the report (Sep 1, 2017 to Sep 30, 2017) so I want result like this:
+--------+---------------------+---------------------+-------+------+
| Object | DtStart | DtStop | FLAGS | User |
+--------+---------------------+---------------------+-------+------+
| First | 2016-09-07 00:00:00 | 2017-09-07 00:00:00 | 1 | Alan |
| First | 2017-09-08 00:00:00 | 2017-09-14 00:00:00 | NULL | NULL |
| First | 2016-09-15 00:00:00 | 2017-09-23 00:00:00 | 2 | John |
| First | 2016-09-24 00:00:00 | 2017-09-30 00:00:00 | NULL | NULL |
+--------+---------------------+---------------------+-------+------+
4 rows in set (0.00 sec)
This is just models of my original tables, I have multiple joins over 10 tables in my query

Remove unforgeted records

I have two tables
CREATE TABLE `server` (
`server_id` int(3) NOT NULL AUTO_INCREMENT,
`server_name` varchar(15),
`server_alias` varchar(50),
`server_status` tinyint(1) DEFAULT '0',
`server_join` tinyint(1) DEFAULT '1',
`server_number_member` int(5),
PRIMARY KEY (`server_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `member` (
`member_id` int(11) NOT NULL AUTO_INCREMENT,
`member_server` int(3) DEFAULT NULL COMMENT 'Id server',
`member_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tên của member',
PRIMARY KEY (`member_id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
An I create table VIEW to get list server
CREATE VIEW `server_client` AS
SELECT
`s`.`server_id` AS `server_id`,
`s`.`server_name` AS `server_name`,
`s`.`server_alias` AS `server_alias`,
IF (`s`.`server_join` = 1, (COUNT(`m`.`member_id`) / `s`.`server_number_member` * 100) DIV 1, 100) AS `server_full`
FROM (`server` `s`
LEFT JOIN `member` `m`
ON ((`m`.`member_server` = `s`.`server_id`)))
WHERE `s`.`server_status` = 1
Now, server table have 1 record:
-------------------------------------------------------------------------------------------
|server_id|server_name|server_alias |server_status|server_join|server_number_member|
|-----------------------------------------------------------------------------------------|
| 1 | SV 01 | http://example.com/ | 0 | 0 | 10 |
-------------------------------------------------------------------------------------------
In membertable
------------------------------------------
| member_id | member_server | member_name|
|----------------------------------------|
| 1 | 1 | aaa |
|----------------------------------------|
| 2 | 1 | bbb |
|----------------------------------------|
| 3 | 1 | ccc |
------------------------------------------
Result in server_client table
--------------------------------------------------------
| server_id | server_name | server_alias | server_full |
|------------------------------------------------------|
| NULL | NULL | NULL | 100 |
--------------------------------------------------------
server_full is used to calculate the percentage of the number of members already in a server
I want to remove record NULL in server_client table
How to do it
Thank
Because you are using COUNT() you should also be aggregating over the servers with GROUP BY. The following query should be along the lines of what you want:
CREATE VIEW server_client AS
SELECT
s.server_id AS server_id,
s.server_name AS server_name,
s.server_alias AS server_alias,
IF (s.server_join = 1,
(COUNT(m.member_id) / s.server_number_member * 100) DIV 1,
100) AS server_full
FROM server s
LEFT JOIN member m
ON m.member_server = s.server_id
WHERE s.server_status = 1
GROUP BY
s.server_id,
s.server_name,
s.server_alias
The only issue you may have is with the sum conditional aggregation I have in my query. In any case, I expect that the results from the above will at least start looking correct.
By the way, I removed all the backticks because you don't them and they are ugly.

MySQL joining tables gives unexpected output

I have been bashing my head against a brick wall trying to get this working and I don't know why it's not.
I join tables A and B using my_field. I then run a sub query to get my_field from table B where complete = 1. This is what I want to use to query tables C and D
This is my current query
SELECT
table_A.*,
table_B.*,
table_C.*,
table_D.*
FROM table_A
INNER JOIN table_B ON
table_A.my_field = table_B.my_field
LEFT JOIN (SELECT my_field FROM table_B WHERE complete ='1') test ON
table_B.my_field = test.my_field
RIGHT JOIN table_C ON
test.my_field = table_C.my_field
INNER JOIN table_D ON
table_C.my_field = table_D.my_field
This is the output of the current query
table_A.field1 | table_A.field2 | table_B.field1 | table_B.field2 | table_C.field1 | table_C.field2 | table_D.field1 | table_D.field2 | test.complete
=============================================================================================================================================================================
something | something | something | something | something | something | something | something | 1
null | null | null | null | something | something | something | something | 0
and this is what i want to get
table_A.field1 | table_A.field2 | table_B.field1 | table_B.field2 | table_C.field1 | table_C.field2 | table_D.field1 | table_D.field2 | test.complete
=============================================================================================================================================================================
something | something | something | something | something | something | something | something | 1
something | something | something | something | null | null | null | null | 0
UPDATE:
Here is the table structures. I've removed columns that have do not link to any other table
CREATE TABLE IF NOT EXISTS `table_A` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
);
CREATE TABLE IF NOT EXISTS `table_B` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
`complete` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `table_C` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
);
CREATE TABLE IF NOT EXISTS `table_D` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
why don't you get rid of the Left Join? This makes it easier to read.
SELECT
table_A.*,
table_B.*,
table_C.*,
table_D.*
FROM table_A
INNER JOIN table_B ON
table_A.my_field = table_B.my_field
LEFT JOIN table_C ON
table_B.my_field = table_C.my_field and table_B.complete ='1'
LEFT JOIN table_D ON
table_C.my_field = table_D.my_field