Reuse joined tabled in a sub query - mysql

Is there any way we can reuse the reference of table we joined in a sub query?
I have three tables:
task_categories, information about task categories
task_priorities, priorities associated with task categories
task_links, url links for each individual tasks.
Please check this SQL Fiddle.
CREATE TABLE task_categories (
task_category_id int,
code varchar(255),
name varchar(255)
);
CREATE TABLE task_priorities (
priority_id int,
task_category_id int,
priority int
);
CREATE TABLE task_links (
task_links_id int,
task_category_id int,
title varchar(255),
link varchar(255),
position int
);
We'd need to join all these tables if we need links of tasks that has high priority. Something like this
select * from task_links t_links
inner join task t on t_links.task_id = t.task_id
inner join task_priorities t_priorities on t.task_id = t_priorities.task_id
where t.code in ('TASK_P2', 'TASK_P3') and
t_priorities.priority = (select min(priority) from task_priorities tp
inner join task t on tp.task_id = t.task_id
where t.code in('TASK_P2', 'TASK_P3'))
order by t_links.position;
Is there any way to optimize this query? This query has joined table twice, I think there should be a better way to write this query.

The logic for your subquery is incorrect. It is not selecting the minimum priority for each task.
I am guessing that you really want:
where t.code in ('TASK_P2', 'TASK_P3') and
tp.priority = (select min(tp2.priority)
from task_priorities tp2
where tp2.task_id = t.task_id
)
This doesn't need much more optimization than an index on task_priorities(task_id, priority).

Related

optimise Yii2 MYSQL query

I need to optimise my code.it works but takes time and sometimes timeouts.
Objective selected columns from table 1 and table 2 must be combined in another table. duplicates are not allowed in the new table. TIA
$modelsc=Customers::find()->select('customer_id')->all();
$modelsp = Product::find()->select('product_no')->all();
foreach($modelsc as $modelc) {
$user = $connection->createCommand(
'SELECT product_no as product_no,:cust_no as fkcustomer_id
FROM product AS p
WHERE NOT EXISTS( SELECT pc.fkproduct_no
FROM
productcustomer AS pc
WHERE
pc.fkproduct_no = p.Product_no AND fkcustomer_id = :cust_no)');
$user->bindValue(':cust_no', $modelc->customer_id);
$modelsx = $user->queryAll();
Yii::$app->db->createCommand()->batchInsert('productcustomer', [ 'fkproduct_no', 'fkcustomer_id'], $modelsx)->execute(); }
looking to your code you could avoid the not exists clause an try using a left join checkn for null on pc.fkproduct_no
SELECT product_no as product_no,
:cust_no as fkcustomer_id
FROM product AS p
LEFT JOIN productcustomer AS pc ON pc.fkproduct_no = p.Product_no
AND fkcustomer_id = :cust_no
WHERE pc.fkproduct_no is null
Anyway be sure you have proper index on the column where of join condition
for table products an index on column Product_no
for table productcustomer a composite index on (fkcustomer_id, fkproduct_no)

MySQL find results based on two different values of the same column

list employees names (Ename) who have both 49008 zip code customers and 49009 zip code customers.
I am struggling to answer the above query based on the above tables.
If the names match between tables assume constraint.
I can filter down to name and zip easily by left joins and group by but struggle after that, I don't know the proper where or having statement. I am assuming it could be done better by a sub query but not sure. Ideally, a single query.
Please and thank you.
1) Create and insert statements for example data:
Create table Employees (EM_Eno INT NOT NULL, EM_Ename VARCHAR(50), EM_Hire_Date DATE, PRIMARY KEY(EM_Eno));
Create table Customers (Customers_Cno INT NOT NULL, Customers_Cname VARCHAR(50), Customers_Street VARCHAR(50), Customers_Zip INT, Customers_Phone INT, primary key(Customers_Cno));
Create table Orders (Orders_Ono INT NOT NULL, Orders_Cno INT, Orders_Eno INT, Orders_Received DATE, Orders_Shipped DATE, primary key(Orders_Ono));
insert into Orders values
( 1,301,501,20161010,20161011);
( 2,302,501,20161011,20161012);
( 3,303,502,20161110,20161111);
( 4,304,502,20161110,20161112);
( 5,305,502,20161110,20161113);
( 6,306,503,20161112,20161114);
( 7,307,501,20161112,20161113);
( 8,308,503,20161112,20161115);
( 9,309,503,20161115,20161120);
(10,300,501,20161112,20161113);
insert into Customers values
(300,'Bryan','100 street',49009,1234567890),
(301,'Ryan','101 street',49008,1234567890),
(302,'Nick','102 street',49009,1234567890),
(303,'Nicholas','103 street',49009,1234567890),
(304,'Alexa','104 street',49009,1234567890),
(305,'Tori','105 street',49008,1234567890),
(306,'Scarlet','106 street',49008,1234567890),
(307,'Heather','100 street',49009,1234567890),
(308,'Amanda','107 street',49008,1234567890),
(309,'James','108 street',49008,1234567890);
insert into Employees values
(501,'Robert',20041010),
(502,'Sam',20050110),
(503,'Brandy',20050710);
2) Ideal end result is answering the query "list employees (names) who have both 49008-zipcode customers and 49009-zipcode customers."
3) Best Attempt thus far:
select Employees.EM_Ename
, Customers.Customers_Zip
from Employees
left
join Orders
on Employees.EM_Eno = Orders.Orders_Eno
left
join Customers
on Orders.Orders_Cno = Customers.Customers_Cno
group
by Employees.EM_Ename
, Customers.Customers_Zip;
The table names are altered slightly in the rextest, because other tables already exist there with the same name...
SELECT e.*
FROM tbl_employees e
JOIN tbl_orders o
ON o.orders_eno = e.em_eno
JOIN tbl_customers c
ON c.Customers_Cno = o.Orders_Cno
WHERE c.Customers_Zip IN(49008,49009)
GROUP
BY e.em_eno
HAVING COUNT(DISTINCT customers_zip) = 2;
http://rextester.com/HCNLU51847
This should be what you want:
select Min(e.Ename)
from #Employees e
join #Orders o on o.Eno = e.Eno
join #Customers c on c.Cno = o.Cno
join #Orders o2 on o2.Eno = e.Eno
join #Customers c2 on c2.Cno = o2.Cno
where o.Ono !=o2.Ono--c.Cno != c2.Cno and
and c.Zip = 49008 and c2.Zip = 49009
group by e.Ename,c.Zip
order by e.Ename
As you can see if you wanted more than those two the self joins become longer.

Getting datas from multiple table in one crystal report

I got 2 tables tbl_issued and tbl_transaction.
tbl_issued has its columns, ItemID,Item,Serial,Quantity and Size. While tbl_transaction has its columns Released,Received,Approved and Department
My problem is I want to get their columns in 1 query, this is mysql query
SELECT `ItemID`,`Item`,`Serial`,`Quantity`,`Size`,`Class`,`Unit`,(SELECT `Released` FROM `tbl_transaction` WHERE `TransactionID` = 12458952) AS `Released`,
(SELECT `Received` FROM `tbl_transaction` WHERE `TransactionID` = 12458952) AS `Received`,
(SELECT `Approved` FROM `tbl_transaction` WHERE `TransactionID` = 12458952) AS `Aprroved`,
(SELECT `Department` FROM `tbl_transaction` WHERE `TransactionID` = 12458952) AS `Department`
FROM `tbl_issued` WHERE `TransactionID` = 12458952
but transferring this on vb.net does not provide output.
Any ideas how i will translate this query to vb.net? Thanks in advance for help!
I don't know what you are trying to do but if you want it to be simplified, here's how. Have you tried Inner Joins? It's like this.
SELECT ItemID, Item, Serial, Quantity, Size, Class, Unit, Released, Received,
Approved, Deparment from tbl_issued a INNER JOIN tbl_transaction b on
a.TransactionID = b.TransactionID Where a.TransactionID = 12458952
I assume that both tables have TransactionID based on your query.

Query to return Pokemon with highest attack by group

I have a database full of Pokemon Cards, and their attacks. I want to do a query to find the Pokemon that has the strongest attack by each type. I want the view to show just the name, type, and damage of the attack.
SELECT p2.MaxD, p2.Type, p1.name
FROM Pokemon p1
INNER JOIN ( SELECT type, MAX(damage) MaxD, pokemon_name FROM Attack GROUP BY Type )
p2 ON p1.type = p2.type AND p2.pokemon_name = p1.name
I have this code. It returns the highest damage but not the correct Pokemon. The Pokemon table doesn't have a damage field. I'm trying to get a grasp of joins.
Here is the structure:
Attack table has 4 fields: pokemon_name (the pokemon this attack belongs to), damage, name (name of the attack), and type (the type of pokemon this attack belongs to).
The Pokemon table has 3: HP, type (of the pokemon), and name (of the pokemon).
First of all you have to build select that select maximal damage for each type (you already have that):
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
Now, this won't have a good performance unless:
type is INT (or ENUM or other numeric type)
there's index on type or type, damage
You cannot select pokemon_name because MySQL doesn't guarantee that you'll get pokemon_name matching MaxD (here's a nice answer on stackoverflow which already covers this issue).
Now you can select pokemon with that matching pokemon_name
SELECT p1.pokemon_name, p1.type, p1.damage
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
GROUP BY (p1.type, p1.damage)
The last GROUP BY statement makes sure that having multiple pokemons with the same attack damage won't cause multiple records for one type,damage pairs.
Again, you will achieve good performance by replacing pokemon_name with pokemon_id. Maybe you should google database normalization for a while [wikipedia],[first tutorial]. You also may want to check this Q&A out, it provides nice overview of what does "relation table" mean.
Now you have correct pokemon_name (for your program sake, I hope you'll replace this with pokemon_id) and you may put it all together:
SELECT p1.pokemon_name, p1.type, p1.damage, p.*
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
INNER JOIN Pokemon p
ON p.pokemon_name = p1.pokemon_name
GROUP BY (p1.type, p1.damage)
Ideal example
In perfect world you're database would look like this:
-- Table with pokemons
CREATE TABLE `pokemons` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255),
-- More fields
PRIMARY KEY (`id`)
)
-- This contains pairs as (1,'Wather'), (2, 'Flame'), ...
CREATE TABLE `AttackTypes` (
`id`,
`name` VARCHAR(255)
)
-- Create records like (1, 2, 3, 152)
-- 1 = automatically generated keys
-- 2 = id of pokemon (let say it's Pikachu :P)
-- 3 = type of attack (this say it's Electric)
-- 152 = damage
-- This way each pokemon may have multiple attack types (Charizard flame + wind)
CREATE TABLE `Attacks` (
`id`,
`pokemonID` INT NOT NULL, -- Represents pokemons.id
`typeID` INT NOT NULL, -- Represents attack.id
`damage` INT
)
ID fields are ALWAYS PRIMARY KEY, NOT NULL and AUTO_INCREMENT in this example
And the select from it, again get types first:
SELECT MAX(attack.damage) AS mDmg, attack.typeID
FROM attack
GROUP BY attack.typeID
Than get pokemon ID:
SELECT a.pokemonID, a.damage, a.typeID
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.typeID
FROM attack AS a
GROUP BY a.typeID
) AS maxA
ON a.typeID = maxA.typeID
AND a.damage = mDmg
GROUP BY (a.typeID)
And once you've covered all that you may actually select pokemon data
SELECT aMax.pokemonID as id,
aMax.damage,
p.name AS pokemonName,
aMax.typeID AS attackTypeID,
t.name AS attackType
FROM (
SELECT a.pokemonID, a.damage, a.type
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.type
FROM attack AS a
GROUP BY a.type
) AS maxA
ON a.type = maxA.type
AND a.damage = mDmg
GROUP BY (a.type)
) AS aMax
INNER JOIN pokemons AS p
ON p.id = aMax.pokemonID
INNER JOIN AttackTypes AS t
ON t.id = aMax.typeID
Performance hints:
you may add field MaxDamage into AttackTypes (which would be calculated by stored procedure) and will save you one level of nasted query
all ID fields should be PRIMARY KEYs
index on Attacks.typeID allows you to quickly get all pokemons capable of that type of attack
index on Attack.damage allows you to quickly find strongest attack
index on Attack.type, Attack.damage (two fields) will be helpful when finding max value for each attack
index on Attack.pokemonID will make look up pokemon -> attack -> attack type name faster
I'm not really sure about your schema but I assumed that the pokemon_name of your attack table is really the name of the pokemon.
SELECT a.*, c.*
FROM Attack a
INNER JOIN
(
SELECT type, MAX(damage) MaxD
FROM Attack
GROUP BY Type
) b ON a.Type = b.Type AND
a.damage = b.MaxD
INNER JOIN Pokemon c
ON c.Name = a.pokemon_name AND
c.Type = a.Type
the above query displays all field from attack table and pokemon table, but If you are really interested on the name, damage and type the you only do query on attack table
SELECT a.*
FROM Attack a
INNER JOIN
(
SELECT type, MAX(damage) MaxD
FROM Attack
GROUP BY Type
) b ON a.Type = b.Type AND
a.damage = b.MaxD

SQL: How to join a view with a table?

UPDATED:
I am using MySQL statement to create a view:
I need to show Editors First Name, Last Name and the City if they shipped more than 50 books. The three tables I have are:
create table editors (
ed_id char(11),
ed_lname varchar(20),
ed_fname varchar(20),
ed_pos varchar(12),
phone varchar(10),
address varchar(30),
city varchar(20),
state char(2),
zip char(5),
ed_boss char(11));
create table titleditors (
ed_id char(11),
title_id char(6),
ed_ord integer);
create table salesdetails (
sonum integer,
qty_ordered integer,
qty_shipped integer,
title_id char(6),
date_shipped date);
Can anyone tell me what code would be to create this result?
I didn't make the tables, I just have to work with what I was given.
Antiquated syntax (note the intermixing of join conditions and filter conditions):
CREATE VIEW qtyorderedview AS
SELECT
salesdetails.title_id, salesdetails.qty_shipped,
editors.ed_id, editors.ed_lname, editors.ed_fname, editors.city
FROM
titleditors, salesdetails, editors
WHERE
titleditors.title_id = salesdetails.title_id
AND editors.ed_id = titleditors.ed_id
AND salesdetails.qty_ordered > 50
Modern syntax (join conditions and filter conditions are separate):
CREATE VIEW qtyorderedview AS
SELECT
salesdetails.title_id, salesdetails.qty_shipped,
editors.ed_id, editors.ed_lname, editors.ed_fname, editors.city
FROM
titleditors
INNER JOIN salesdetails ON titleditors.title_id = salesdetails.title_id
INNER JOIN editors ON editors.ed_id = titleditors.ed_id
WHERE
salesdetails.qty_ordered > 50
Joins against views work exactly like joins against tables. Just use the view name in place of a regular table name.
SELECT e.*
FROM (
SELECT DISTINCT te.ed_id
FROM (
SELECT title_id
FROM sales_details
GROUP BY
title_id
HAVING SUM(qty_shipped) > 50
) t
JOIN titleditors te
ON te.title_id = t.title_id
) te
JOIN editors e
ON e.ed_id = te.ed_id