I have a base query which uses a view which uses another view, like this.
SELECT a,b,c,DEBIT_AMOUNT, CREDIT_AMOUNT FROM MAIN_VIEW WHERE a='foo' AND c='bar';
Here's the schema
create table BASE_TABLE (
id int not null auto_increment,
a varchar(20),
b varchar(20),
c varchar(20),
primary key (id));
create table OTHER_TABLE (
oid int not null auto_increment,
id int not null,
mtype varchar(10),
amount varchar(20),
primary key (oid));
create or replace view `MAIN_VIEW` AS
SELECT BT.a, BT.b, BT.c,SUB_VIEW.DEBIT_AMOUNT, SUB_VIEW.CREDIT_AMOUNT
FROM BASE_TABLE BT
LEFT JOIN SUB_VIEW ON SUB_VIEW.id = BT.id
create or replace view `SUB_VIEW` AS
SELECT BT.id,
( SELECT SUM(O.amount)
FROM OTHER_TABLE O
WHERE O.mtype = 'DR'
AND O.id = BT.id
) AS DEBIT_AMOUNT,
( SELECT SUM(O.amount)
FROM OTHER_TABLE O
WHERE O.mtype = 'CR'
AND O.id = BT.id
) AS CREDIT_AMOUNT
FROM BASE_TABLE BT
My query is permformance is very slow, to speed up query execution, i've modified the MAIN_VIEW like this
since the BASE_TABLE is already available on MAIN_VIEW, i thought fetching DEBIT_AMOUNT and CREDIT_AMOUNT from then and there rather than going into the SUB_VIEW
-- MAIN_VIEW ---
create or replace view `MAIN_VIEW` AS
SELECT BT.a, BT.b, BT.c,
( SELECT SUM(O.amount)
FROM OTHER_TABLE O
WHERE O.mtype = 'DR'
AND O.id = BT.id
) AS DEBIT_AMOUNT,
( SELECT SUM(O.amount)
FROM OTHER_TABLE O
WHERE O.mtype = 'CR'
AND O.id = BT.id
) AS CREDIT_AMOUNT
FROM BASE_TABLE BT
But after this modification, query performance is even worse.. can any one help? I thought subviews are be bad for performance...
You need INDEX(id, mtype) (in either order). This should make the subqueries faster, hence the entire query faster.
Related
I'm trying to get this to work. When I run the SELECT on the whole dataset I know that the record with cust_number shows up in position 6 (When Using ORDER BY) but this code returns position 37327 which is it's non ordered by position.
SELECT
x.position,
x.cust_number,
x.company,
x.surname,
x.first_name,
x.title
FROM
(SELECT
#rownum:=#rownum + 1 AS position,
c.cust_number,
company,
surname,
first_name,
title
FROM
1_customer_records c
LEFT JOIN addresses a ON c.fk_addresses_id = a.id
JOIN (SELECT #rownum:=0) r
ORDER BY a.company , c.surname , c.first_name , c.title) x
WHERE
x.cust_number = 43246;
Here is another approach using a temp table
CREATE TEMPORARY TABLE row_calc (id INT AUTO_INCREMENT, fk INT NULL, PRIMARY KEY (id)) ENGINE=MEMORY;
INSERT INTO row_calc(fk)
SELECT
cust_number
FROM
1_customer_records c
LEFT JOIN
addresses a ON c.fk_addresses_id = a.id
ORDER BY company,surname,first_name,title;
SELECT
id
FROM
row_calc
WHERE
fk = 43246 LIMIT 1;
DROP TABLE row_calc;
First İ use wherehas but then I decided use this way. This way result better than wherehas but It isn't satisfy me. Query response time is a 873ms. So I have 400k+ data in the table.
select count(*) as aggregate
from `orders`
where (`pickup_address_id` in (
select `id`
from `addresses`
where `region_id` = 12)
or `delivery_address_id` in (
select `id`
from `addresses`
where `region_id` = 12)
) and `orders`.`status` = 2
Try this:
select count(distinct o.`id`) as aggregate
from `orders` o
inner join `addresses` a ON a.`id` IN (o.`pickup_address_id`, o.`delivery_address_id`)
AND a.`region_id` = 12
where o.`status` = 2
Alternatively:
SELECT count(distinct id) as aggregate
FROM (
select o.`id`
from `orders` o
inner join `addresses` a ON a.`id` = o.`pickup_address_id`
AND a.`region_id` = 12
where o.`status` = 2
UNION
select o.`id`
from `orders` o
inner join `addresses` a ON a.`id` = o.`delivery_address_id`
AND a.`region_id` = 12
where o.`status` = 2
) t
But I don't know you'll improve much to look through 400K rows in less than a second.
First, you can try to eliminate multiple (twice, to be more precise) same subquery evaluation using a Common Table Expression
WITH CTE(id) AS (
SELECT id
FROM addresses
WHERE region_id = 12
)
This CTE would be evaluated once.
Second, get row count from orders table joined with cte on existence of pickup_address_id and delivery_address_id in cte.
WITH CTE(id) AS (
SELECT id
FROM addresses
WHERE region_id = 12
)
SELECT COUNT (*)
FROM orders
CROSS JOIN CTE ON CTE.id = orders.delivery_address_id
OR CTE.id = orders.pickup_address_id
Finally, add filter by status = 2 and query would be like
WITH CTE(id) AS (
SELECT id
FROM addresses
WHERE region_id = 12
)
SELECT COUNT (*)
FROM orders
CROSS JOIN CTE ON CTE.id = orders.delivery_address_id
OR CTE.id = orders.pickup_address_id
WHERE orders.status = 2
Also you should have the following indexes:
addresses table:
INDEX (region_id)
orders table:
INDEX (pickup_address_id),
INDEX (delivery_address_id),
INDEX (status)
Give it a try.
With empty tables I've got this
Schema (MySQL v8.0)
create table addresses (
id int primary key,
region_id int not null,
index(region_id)
);
create table orders (
id int primary key,
pickup_address_id int,
delivery_address_id int,
status int not null,
index (pickup_address_id),
index (delivery_address_id),
index(status),
foreign key (pickup_address_id) references addresses(id),
foreign key (delivery_address_id) references addresses(id)
);
Query #1
explain with cte(id) as (
select id from addresses where region_id = 12)
select count(*) from orders
cross join cte on cte.id = orders.delivery_address_id
or cte.id = orders.pickup_address_id
where status = 2;
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
orders
ref
pickup_address_id,delivery_address_id,status
status
4
const
1
100
1
SIMPLE
addresses
ref
PRIMARY,region_id
region_id
4
const
1
100
Using where; Using index
Write a SQL query that could produce this query plan.
CREATE TABLE Slot(
sid int PRIMARY KEY,
wall VARCHAR(30),
x float,
y float
)
CREATE TABLE Placement(
rid int REFERENCES Route(rid),
hid int REFERENCES Hold(hid),
sid int REFERENCES Slot(sid),
PRIMARY KEY(rid, hid, sid)
)
Would this sql query be able to produce this plan?
Select *
FROM Placement P, Slot S
WHERE P.sid = S.sid
Do a FROM table then JOIN anotherTable and then the condition after ON
Select *
FROM Placement P
INNER JOIN Slot S
ON P.sid = S.sid
I have next tables:
CREATE TABLE IF NOT EXISTS `Customers` (
`id` INT AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE IF NOT EXISTS `Orders` (
`id` INT AUTO_INCREMENT,
`id_cust` INT NOT NULL,
`descr` VARCHAR(40),
`price` INT NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`id_cust`) REFERENCES `Customers`(`id`)
);
One customer can have many orders. I want to get id_cust and sum of the orders of who paid the most(one person).
My query:
SELECT cust, max_orders_sum
FROM
(
(
SELECT MAX(orders_sum) AS max_orders_sum
FROM (
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
) AS same_query0
) AS step1
INNER JOIN
(
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
) AS same_query1
ON (step1.max_orders_sum = same_query1.orders_sum)
);
Main problem:
as you can see, it has the same parts: same_query0 and same_query1. Is there any way to get rid of them?
Or if you know the better way to reach my goal, please share.
I found one simple solution:
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
ORDER BY orders_sum DESC LIMIT 1;
But this is not a direct way to solve the problem.
I don't think you can do much better than what you've already done, unless you create a view like
create view v_cust_tot as
select id_cust, sum(price) as cust_tot
from Orders
group by id_cust
With that you'd be able to rewrite your query like this
select id_cust, cust_tot
from v_cust_tot
where cust_tot = (select max(cust_tot) from v_cust_tot)
This would be an improvement just in the compactness of the query, because I think performances would be the same as the execution plan would be almost identical
Another one nice solution:
select id_cust, sum(price) from orders group by id_cust having sum(price) =
(select max(prc) from
(select sum(price) as prc from orders group by id_cust) as tb);
me trying to make distinct data in temporary table, trying to simple it with create table #tabletemp still got wrong, it says unrecognize data type near distinct and comma or closing bracket was expected near ponumber
here's the code :
CREATE TEMPORARY TABLE t1(
SELECT DISTINCT
PONumber varchar(10),
POdate varchar(10),
customername varchar(35),
description varchar(22)
FROM tb_po
);
SELECT
p.PONumber,
p.podate,
p.customername,
p.description,
(
SELECT SUM(q.deliveryqty)
FROM tb_spb q
WHERE p.PONumber = q.PONumber AND p.description = q.description
) AS Total
FROM t1 p
If you really need it to be in a temporary table, another approach is using "SELECT INTO" wherein you wont need to declare the creation of a temporary table. (Although creating a table then inserting records is the more preferred method https://stackoverflow.com/a/6948850/6344844)
SELECT DISTINCT
p.PONumber,
p.POdate,
p.customername,
p.[description],
SUM(q.deliveryqty)
INTO #TEMPTABLE_NAME
FROM tb_po p
INNER JOIN tb_spb q
ON p.PONumber = q.PONumber
AND p.description = q.description
GROUP BY p.PONumber,p.POdate,p.customername,p.[description]
SELECT * FROM #TEMPTABLE_NAME
DROP TABLE #TEMPTABLE_NAME
You don't need to create a temporary table to get the result that you want. Here is my revised query based on your query:
SELECT DISTINCT
p.PONumber,
p.POdate,
p.customername,
p.[description],
SUM(q.deliveryqty)
FROM tb_po p
INNER JOIN tb_spb q
ON p.PONumber = q.PONumber
AND p.description = q.description
GROUP BY p.PONumber,p.POdate,p.customername,p.[description]
You have to first create the table then insert into the table. For SQL server You can do like this.
CREATE TABLE TEMPORARY(
PONumber varchar(10),
POdate varchar(10),
customername varchar(35),
description varchar(22)
);
insert into TEMPORARY SELECT DISTINCT
PONumber varchar(10),
POdate varchar(10),
customername varchar(35),
description varchar(22)
FROM
tb_po ;
If you want to create temp table in SQL server then you have use # before table name.
For reference http://www.c-sharpcorner.com/uploadfile/b19d5a/temporary-and-global-temporary-table-in-sql-server-2008/