Mysql query optimization issue - mysql

I am facing an query optimizing problem. Hope I will get some help.
The scenario is I have 4 tables.
Table 1 [asset_id, asset_name, User_id].
Table 2 [company_id, Company_name]
Table 3 [User_id, User_name]
Table 4 [Map_id, User_id, Company_id ]
My result will be to check Users from same company cannot able to upload same asset, checking of asset will be on COmpany level.
I have already written a query, that serves the purpose as well, but i need to eliminate the sub query for optimizing purpose.
The query I have written has a sub query.
My query is
SELECT COUNT(tg.asset_id)
FROM Table 1 tg
INNER JOIN Table 4 mcu
ON ((tg.User_id = mcu.User_id )
AND mcu.Company_id = (select Table 4.Company_id
from Table 4
where Table 4.User_id = 1))
WHERE tg.asset_name = 't1' ;

Hope this may be helpful::
SELECT
COUNT(tg.asset_id)
FROM Table 1 tg
INNER JOIN Table 4 mcu ON (tg.User_id = mcu.User_id and Company_id = ClientID )
WHERE tg.asset_name = 't1' and mcu.User_id = 1

The subquery seems unneeded for this query since it also uses Table 4.
SELECT COUNT(tg.asset_id) FROM Table 1 tg
INNER JOIN Table 4 mcu ON tg.User_id = mcu.User_id
WHERE tg.asset_name = 't1'
AND mcu.ClientID = mcu.Company_id
AND mcu.User_id = 1;

Related

Mysql Left Join of 3 tables and a one being joined twice

I need to do a join of 3 tables.
They all have an ID value, the issue is:
Table 1
Is the main one, name : cnpj_cnae and the ID is CNPJ_CNAE
Table 2
I need to get four columns from this one (Up to here I already got it).
The ID here is CNPJ_CNAE
Table 3
Here's the issue. For each ID value on table 1 I can have more than 1 even 10 on this table. I need to get 4 columns from ONLY THE FIRST TWO of this table.
The ID here is CNPJ_SOCIO
The finishing table would need to look like
ID INFOTABLE1 INFOTABLE2 INFOTABLE3ROW1 INFOTABLE3ROW2
I have tried some joins, but being a relative newbie with mysql I am suffering >.<
I have tried to do this:
CREATE TABLE cnpj_cnae_emp_test3 AS (
SELECT
`cnpj_cnae_test2`.*,
`cnpj_soci`.NOME_SOCIO,`cnpj_soci`.CNPJ_CPF_SOCI,`cnpj_soci`.ID_QUALIFICACAO_REP,`cnpj_soci`.DESC_QUALIFICACAO_REP,
`cnpj_emp_02`.MUNICIPIO,`cnpj_emp_02`.BAIRRO,`cnpj_emp_02`.TIPO_LOGRADOURO,`cnpj_emp_02`.LOGRADOURO,`cnpj_emp_02`.NUMERO,`cnpj_emp_02`.COMPLEMENTO
FROM `cnpj_cnae_test2`, `cnpj_soci`, `cnpj_emp_02`
WHERE `cnpj_cnae_test2`.CNPJ_CNAE = `cnpj_soci`.CNPJ_SOCIO AND `cnpj_cnae_test2`.CNPJ_CNAE = `cnpj_emp_02`.CNPJ
);
The issue is this will generate extra rows for every SOCI where instead of 1 soci on each one I need 2 socis on each line.
Table 1:
Layout of CNAE table
Table 2:
Layout of EMP table
Table 3:
Layout of Socio table
The resulting table would look like this:
More detailed end result Final layout with descriptions
Layout of result
In the end this by Owl was exactly what I needed:
WITH soci_partition AS (
SELECT CNPJ_SOCIO
,NOME_SOCIO
,CNPJ_CPF_SOCIO
,ID_QUALIFICACAO
,DESC_QUALIFICACAO_SOCIO
,row_number() OVER (PARTITION BY CNPJ_SOCIO ORDER BY IDX ASC) AS rownum
FROM cnpj_soci
)
SELECT * -- add actual columns
FROM cnpj_cnae_test2 AS cnae
INNER JOIN cnpj_emp_02 AS emp -- left join instead? depends on table structure
ON cnae.ID_CNAE = emp.IDX
LEFT JOIN soci_partition AS soci1
ON soci1.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci1.rownum = 1
LEFT JOIN soci_partition AS soci2
ON soci2.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci2.rownum = 2
Not sure if I got the joins/partition right, the foreign keys weren't clear.
Even if it's not perfect, hopefully this explains the general theory. Please edit in your correct answer if you end up debugging.
Also don't use old-style joins. You'll immediately fail interviews on that alone; they were deprecated 25 years ago.
WITH soci_partition AS (
SELECT CNPJ_SOCIO
,NOME_SOCIO
,CNPJ_CPF_SOCIO
,ID_QUALIFICACAO
,DESC_QUALIFICACAO_SOCIO
,row_number() OVER (PARTITION BY CNPJ_SOCIO ORDER BY IDX ASC) AS rownum
FROM cnpj_soci
)
SELECT * -- add actual columns
FROM cnpj_cnae_test2 AS cnae
INNER JOIN cnpj_emp_02 AS emp -- left join instead? depends on table structure
ON cnae.ID_CNAE = emp.IDX
LEFT JOIN soci_partition AS soci1
ON soci1.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci1.rownum = 1
LEFT JOIN soci_partition AS soci2
ON soci2.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci2.rownum = 2
Give a try to below query
select * from cnae t1
left join emp t2 on t1.cnpj_cnae = t2.cnpj
left join socio t3 on t1.cnpj_cnae = t2.cnpj_socio
group by idx_cnae ;

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 - expensive subquery

I have this query that was taking 13 seconds to run, consistently:
SELECT COUNT(*) AS Counter
FROM Groups_Users gu
JOIN Groups g ON gu.GroupID = g.GroupID
WHERE (
(gu.UserID = 3 AND gu.IsAdmin = 1 AND g.GroupID = 395)
OR g.OrganizationID IN (
SELECT OrganizationID FROM Organizations_Users WHERE UserID = 3 AND IsAdmin = 1 )
)
After adding indexes to Groups_Users and Organization_Users, it went to less than 1 second. So, problem solved, except that I expect the number of records in these 2 tables to grow a lot. And I'm wondering if my subselect and/or join is inefficient and could be made better. Maybe at some point the performance will degrade again when the record count goes from 8,000 to 100,000 because the subselect is inefficient.
Any ideas on making this query better?
SELECT COUNT(*) AS Counter
FROM Groups_Users gu
JOIN Groups g ON gu.GroupID = g.GroupID
WHERE
(
( gu.UserID = 3
AND gu.IsAdmin = 1
AND g.GroupID = 395
)
OR EXISTS
( SELECT 1
FROM Organizations_Users ou
WHERE ou.UserID = 3
AND ou.IsAdmin = 1
AND ou.organizationid = g.organizationid
)
)
basically you say select 1 where these conditions are met.. if they are true then the exists query is true and it will include it. so all you have to do is add AND ou.organizationid = g.organizationid to the subquery and you're good to go

MySQL select in update statement

This MySQL statement give me all id_duel_player for player with id_player=30 and it work fine.
SELECT b.id_duel_player
FROM duels a
INNER JOIN duel_player b
ON a.id_duel = b.id_duel
WHERE id_player = 30
UNION ALL
SELECT c.id_duel_player
FROM duel_player c
INNER JOIN
(
SELECT aa.*
FROM duels aa
INNER JOIN duel_player bb
ON aa.id_duel = bb.id_duel
WHERE bb.id_player = 30
) d ON c.id_duel = d.id_duel AND c.id_player <> 30
I want to make MySQL statement for UPDATE (fields from duel_player table) all of this id_duel_player that returns this select statement.
UPDATE duel_player
SET num = 2,
total = 5
WHERE (duel_player.id_duel_player = id_duel_player's from above SELECT statement)
I want most effective and fastest way to do this.
Thanks
For 200-400 rows it's likely fastest to create a temporary table with the results, and then do the UPDATE with a join:
CREATE TEMPORARY TABLE id_duel_players AS
SELECT b.id_duel_player as id FROM duels a ...
UPDATE duel_player
JOIN id_duel_players ON duel_player.id_duel_player = id_duel_players.id
SET num = 2,
total = 5
For smaller result sets you may find the IN operator sufficiently fast (... WHERE id_duel_player IN (SELECT ...)), but I've found it unreliable for result sets with hundreds of rows. (Unreliable = suddenly no matches are found, no idea why, I haven't investigated.)

How to left join or inner join a table itself

I have this data in a table, for instance,
id name parent parent_id
1 add self 100
2 manage null 100
3 add 10 200
4 manage null 200
5 add 20 300
6 manage null 300
How can I left join or inner join this table itself so I get this result below?
id name parent
2 manage self
4 manage 10
6 manage 20
As you can I that I just want to query the row with the keyword of 'manage' but I want the column parent's data in add's row as the as in manage's row in the result.
Is it possible?
EDIT:
the simplified version of my actual table - system,
system_id parent_id type function_name name main_parent make_accessible sort
31 30 left main Main NULL 0 1
32 31 left page_main_add Add self 0 1
33 31 left page_main_manage Manage NULL 0 2
my actual query and it is quite messy already...
SELECT
a.system_id,
a.main_parent,
b.name,
b.make_accessible,
b.sort
FROM system AS a
INNER JOIN -- self --
(
SELECT system_id, name, make_accessible, sort
FROM system AS s2
LEFT JOIN -- search --
(
SELECT system_id AS parent_id
FROM system AS s1
WHERE s1.function_name = 'page'
) AS s1
ON s1.parent_id = s2.parent_id
WHERE s2.parent_id = s1.parent_id
AND s2.system_id != s1.parent_id
ORDER BY s2.sort ASC
) b
ON b.system_id = a.parent_id
WHERE a.function_name LIKE '%manage%'
ORDER BY b.sort ASC
result I get currently,
system_id main_parent name make_accessible sort
33 NULL Main 0 1
but I am after this,
system_id main_parent name make_accessible sort
33 self Main 0 1
You just need to reference the table twice:
select t1.id, t1.name, t2.id, t2.name
from TableA t1
inner join TableA t2
on t1.parent_id = t2.Id
Replace inner with left join if you want to see roots in the list.
UPDATE:
I misread your question. It seems to me that you always have two rows, manage one and add one. To get to "Add" from manage:
select system.*, (select parent
from system s2
where s2.parent_id = system.parent_id
and s2.name = 'add')
AS parent
from system
where name = 'manage'
Or, you might split the table into two derived tables and join them by parent_id:
select *
from system
inner join
(
select * from system where name = 'add'
) s2
on system.parent_id = s2.parent_id
where system.name = 'manage'
This will allow you to use all the columns from s2.
Your data does not abide to a child-parent hierarchical structure. For example, your column parent holds the value 10, which is not the value of any id, so a child-parent association is not possible.
In other words, there's nothing that relates the record 2,manage,null to the record 1,add,self, or the record 4,manage,null to 3,add,10, as you intend to do in your query.
To represent hierarchical data, you usually need a table that has a foreign key referencing it's own primary key. So your column parent must reference the column id, then you can express a child-parent relationship between manage and add. Currently, that's not possible.
UPDATED: Joining by parent_id, try:
select m.id, m.name, a.parent
from myTable m
join myTable a on m.parent_id = a.parent_id and a.name = 'add'
where m.name = 'manage'
Change the inner join to a left join if there may not be a corresponding add row.