I wrote an example to describe my problem in a more complex database.
I use MySQL 5.7 with Delphi XE8.
I have 2 tables:
CREATE TABLE customers
(ID INT NOT NULL AUTO_INCREMENT ,
Name VARCHAR(30) NOT NULL ,
PRIMARY KEY (ID)
) ENGINE = InnoDB;
CREATE TABLE orders
(IDorders INT NOT NULL AUTO_INCREMENT ,
customerID INT NOT NULL ,
Description VARCHAR(30) NOT NULL ,
DateOrder Date NOT NULL ,
PRIMARY KEY (IDorders),
INDEX DateOrderIndex (DateOrder, customerID) USING BTREE;
) ENGINE = InnoDB;
select *,
(SELECT MAX(DateOrder) FROM Orders WHERE Orders.customerID =
customers.ID) as LastOrder
FROM customers
My problem is:
the customer table has 58,000 records
and
the orders table has 200,000 records
The query result occurs after 28 seconds.
Where am I doing wrong?
You can try to use JOIN with MAX and GROUP BY in subquery.
select c.*,
t1.maxDt
FROM customers c
JOIN (
SELECT customerID,MAX(DateOrder) maxDt
FROM Orders
GROUP BY customerID
) t1 on t1.customerID = c.ID
Note
if your query is slow you can try to create indexs on Orders.customerID and
customers.ID
Related
I'm new to MySQL and trying to select the total number of orders for each user in the orders table. Also, I'm trying to order the results by most orders to the least orders.
This is what I have for order table
CREATE TABLE `order` (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
created TIMESTAMP NOT NULL DEFAULT NOW(),
user_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE RESTRICT
);
And this is what I have for user table
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
first_name VARCHAR(255),
last_name VARCHAR(255),
created TIMESTAMP NOT NULL DEFAULT NOW()
);
I have referenced other resources and tried the following, but I'm getting an error..Not sure how I can go about it?
SELECT COUNT(DISTINCT order_id) AS user_id
FROM `order`
GROUP BY user_id
this will
group by user_id
then count all lines
you will get one line per user_id with the count
ps: distinct is useless because order.id is an autoincrement key.
select user_id, count(*) as nb
from order
group by user_id
Or this if you want users with no orders included.
you need to start from user table, join all related orders, and basically do the same counting and grouping as before.
select u.id, count(*) as nb
from user as u
left join `order` as o on o.user_id = u.id
group by u.id
If there is a result matching the value of "parent_id" in my "table1" and "table2" tables, I want to get the number of rows in the "table1" table.
But the SQL query takes too long.
There are 100 thousand rows in table1.
There are 40 thousand rows in table2.
A table data file for you to try
See: https://pastebin.pl/view/raw/ddf8c467
Table Structure
CREATE TABLE table1 (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
parent_id INT(11) UNSIGNED NOT NULL ,
tes1 INT(1) NOT NULL , PRIMARY KEY (id)) ENGINE = MyISAM;
CREATE TABLE table2 (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
parent_id INT(11) UNSIGNED NOT NULL ,
tes2 INT(1) NOT NULL , PRIMARY KEY (id)) ENGINE = MyISAM;
SQL Query I use
SELECT COUNT(A.id) AS total
FROM table1 A
LEFT JOIN table2 B ON A.parent_id = B.parent_id
WHERE B.id IS NOT NULL
create a index on parent_id on table B and use INNODB if possible.
you can also use inner join
SELECT COUNT(A.id) AS total
FROM table1 A
INNER JOIN table2 B ON A.parent_id = B.parent_id;
I'm having some trouble trying to make a view with a calculated average column, for every movie row I need an average rating based on all the ratings for this movie in the rating table.
Movie table:
CREATE TABLE IF NOT EXISTS streaming_db.movie(
id BIGINT NOT NULL auto_increment
,name VARCHAR (100)
,description VARCHAR (1000)
,PRIMARY KEY (id)
) engine = InnoDB;
Rating table:
CREATE TABLE IF NOT EXISTS streaming_db.rating(
id BIGINT NOT NULL auto_increment
,rating_score DECIMAL(4, 2) NOT NULL
,comment VARCHAR (255) NULL
,id_profile BIGINT NOT NULL
,id_movie BIGINT NOT NULL
,PRIMARY KEY (id)
) engine = InnoDB;
Here's what I have so far:
CREATE VIEW streaming_db.midia
AS
SELECT name,
description
FROM streaming_db.movie a
INNER JOIN (SELECT avg(rating_score) AS averageRating from streaming_db.rating where
rating.id_movie = a.id);
It was telling me that a derived table needs its own alias, and I don't know if that really gives me the average per row.
You are attempting a correlated subquery in the FROM clause. Well, this is actually a real thing, called a lateral join.
But that is not your intention. Move the logic to the SELECT:
SELECT m.name, m.description,
(SELECT avg(rating_score)
FROM sistema_streaming_db.rating r
WHERE r.id_movie = m.id
) as averageRating
FROM streaming_db.movie m;
Note that I fixed the table aliases so they are abbreviations for the table names, which makes the query much easier to read.
I want to delete some rows in a table. However the delete condition is not direct.This is just 1 table CUSTOMER:
CREATE TABLE customer (
CUST_ID int(11) NOT NULL AUTO_INCREMENT,
PRODUCT_CODE varchar(50) NOT NULL,
USER_NAME varchar(150) NOT NULL,
PRIMARY KEY (CUST_ID)
) ENGINE=InnoDB AUTO_INCREMENT=2455046 DEFAULT CHARSET=latin1;
Here is what works for me:
select cust_id from CUSTOMER where user_name = '20012';
Then use the resulting cust_id in the following DELETE ($cust_id is result of query 1)
delete from CUSTOMER where cust_id = $cust_id
Note: This can only delete one user_name . Also I want to do this in 1 query, not 2.
CUSTOMER t1
LEFT JOIN (
SELECT
cust_id id
FROM
CUSTOMER
where
user_name IN ('20012', '20013', '20014')
) t2 ON t1.cust_id = t2.id
The above apparently did not work.
You are unnecessarily complicating the logic. Instead of first getting all the customer id(s), for the given user_name values, you could simply filter on the user_name values. It is simply this:
DELETE FROM CUSTOMER WHERE user_name IN ('20012', '20013', '20014')
I have a bit complicated query:
SELECT SQL_CALC_FOUND_ROWS DISTINCT l1.item_id, l1.uid, l2.id, l2.uid, u.prename, l1.item_id, l2.item_id,
(SELECT SUM(cnt) FROM
(
SELECT DISTINCT
p1.item_id,
COUNT(*) AS cnt
FROM pages_likes AS p1
JOIN pages_likes AS p2 ON p1.item_id = p2.item_id AND p1.status = p2.status
WHERE p1.uid = 391 AND p2.uid = 1091
GROUP BY p1.id
ORDER BY p1.date DESC
) AS t) AS total
FROM pages_likes l1
JOIN users u on u.id = l1.uid
JOIN pages_likes l2 on l1.item_id = l2.item_id
JOIN users_likes ul on l1.uid = ul.uid
WHERE ul.date >= DATE_SUB(NOW(),INTERVAL 1 WEEK)
AND l1.uid != 1091 AND l2.uid = 1091
AND (l1.status = 1 AND l2.status = 1)
AND u.gender = 2
GROUP BY l1.uid
ORDER BY
total DESC,
l1.uid DESC,
l1.date DESC
What I expect: It should display all users, sorted by total page likes we have in common that also are the most liked users this week.
The thing is that I inserted values (391 and 1091) as user id to test the query. But since it should be dynamic I'll need to use the row of the first query l1.uid in the subquery, so it should be WHERE p1.uid = l1.uid AND p2.uid = 1091 but mysql can't find the row.
status = 1 means user liked this page, status = 0 means user disliked this page.
Table structure here:
CREATE TABLE pages_likes
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
uid INT NOT NULL,
date DATETIME NOT NULL,
item_id INT,
status TINYINT
);
CREATE INDEX item_index ON pages_likes (item_id);
CREATE INDEX uid_index ON pages_likes (uid);
CREATE TABLE users
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
fb_uid VARCHAR(255),
email VARCHAR(100) NOT NULL,
pass VARCHAR(50) NOT NULL,
gender TINYINT NOT NULL,
birthdate DATE,
signup DATETIME NOT NULL,
lang VARCHAR(10) NOT NULL,
username VARCHAR(255),
prename VARCHAR(255) NOT NULL,
surname VARCHAR(255) NOT NULL,
projects VARCHAR(255) NOT NULL,
views INT DEFAULT 0,
verified DATETIME
);
CREATE UNIQUE INDEX id_index ON users (id);
CREATE INDEX uid_index ON users (id);
CREATE TABLE users_likes
(
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
uid INT NOT NULL,
date DATETIME NOT NULL,
item_id INT,
status TINYINT
);
CREATE INDEX item_index ON users_likes (item_id);
CREATE INDEX uid_index ON users_likes (uid);
Have you tried different alias names for your subquery and then use alias from outer query? It works for me in this simple example: http://rextester.com/MJOL87502
Sadly I cannot test in your sqlfiddle, since that site often doesn't respond or throws errors (like it does now).
You could also use Window Functions and replace your subselect with something as simple as SUM(*) OVER (PARTITION BY p1.id, p1.item_id), but MySQL does not support Window Functions.