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

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 ;

Related

MySQL - unique rows, corresponding to one of 3 tables only

The following query pulls data correctly as expected, however the left join with lnk_cat_isrc table and through that to catalogue table, brings back repeated data if there is more than one item in catalogue which has the same isrcs from isrc table:
SELECT
isrc.ISRC,
isrc.Track_Name,
isrc.ArtistName,
isrc.TitleVersion,
isrc.Track_Time,
`isrc_performer`.`PerformerName` ,
`performer_category`.`PerformerCategory` ,
`isrc_performer`.`PerformerRole` ,
`isrc`.`isrc_ID`,
`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer` ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category` ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc` ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue` ON (catalogue.ID = lnk_cat_isrc.cat_id)
ORDER BY isrc_ID desc LIMIT 0 , 10
";
I cannot use group by on isrc, because the isrc_performer table can have more than one performer to an isrc.
So the relations are like this:
Few items from catalogue table can have several identical items from isrc table. In turn, each isrc can have more than one entry in isrc_performer table.
What I want is to display all corresponding data from isrc_performer in relation to each isrc, but not repeating it for each item from catalogue table.
I also want to display all the rest "empty" isrcs (those which don't have any data in isrc_performer table)
Can you give me any ideas?
P.S. despite I'm not pulling any data from catalogue table itself, I'm using it to search by a catalogue number, when user defines search criteria for $where_condition variable, hence I need to keep it in the query.
i.e. $where_condition = "catalogue.Catalogue LIKE '%test%' OR ISRC LIKE '%test%' OR Track_Name LIKE '%test%' OR ArtistName LIKE '%test%' OR TitleVersion LIKE '%test%' OR PerformerName LIKE '%test%' OR PerformerCategory LIKE '%test%' OR PerformerRole LIKE '%test%'";
------UPD:
trying to graphically represent possible variation in these 3 tables relations:
cat1 - isrc1 - performer1
isrc2 - performer1
- performer2
- performer3
cat2 - isrc2 - performer1
- performer2
- performer3
- isrc3 - performer2
- performer4
cat3 - isrc4
- isrc1 - performer1
UPD (pics added)
Here are screen prints. As you can see on picture 1 there are 9 rows with same isrc number, however there are 3 repeated performers Jason, David, Paul.
This is because 3 different catalogue items have this exact isrc with 3 different performers as per pic 2
= 1(isrc) * 3(catalogue) * 3(performers) = 9 row on output
All I want is that Performers grid would only display 3 rows of this isrc for each performer.
---Rearrange the answer to put the "best" option up top.. .but is all of this for naught.. w/o any data from lnk_cat_isrc or catalogue being returned, why does filtering on catalog make a difference? we're returning all isrc regardless of any filtering because it's a left join...
So this brings into question given sample data what are the expected results.
Possibly more elegant... (but not sure if it would be faster) moving away from exists and simply using a distinct in a subquery so catalog queries always return 1 row per isrc; solving the 1-M problem keeping the left join thereby keeping the isrc records not in the catalog limits.
Return all isrc information performer information if it exists, performer category info if it exists and catalogue information If, and only if it matches the catalog filters.
SELECT isrc.ISRC
, isrc.Track_Name
, isrc.ArtistName
, isrc.TitleVersion
, isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON isrc.isrc_ID = isrc_performer.isrc_ID
LEFT JOIN `performer_category`
ON performer_category.PerfCat_ID = isrc_performer.PerfCat_ID
LEFT JOIN (SELECT distinct lnk_cat_isrc.isrc_ID
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
WHERE...) DCat
ON Dcat.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10;
As you pointed out the join is causing the problem. So eliminate the join and use the exists notation. Distinct would also work since you're not selecting any values from catalog; though exists should be faster.
Fast but doesn't include all isrc records... (not sure why the or not exists should bring them back in...)
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
WHERE EXISTS (SELECT *
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
--and your other criteria
WHERE (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
)
OR NOT EXISTS (SELECT *
FROM `lnk_cat_isrc`
WHERE lnk_cat_isrc.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10
Or using select distinct simple straight forward; but slow
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc`
ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue`
ON (catalogue.ID = lnk_cat_isrc.cat_id)
--AND (other criteria on catalog here, cause in a where clause you left joins will behave like inner joins)
ORDER BY isrc_ID desc
LIMIT 0 , 10;

Count field and group by regarding another field on same table MySQL

I am trying to find count of field by another field in same table on MySQL.My Table Like This:
Id DrgId CodeType IsPrincipal Code
182250051 48261836 I 1 T151
182250055 48261836 I 2 U739
182250059 48261836 I 3 Y929
182250061 48261836 I 4 W444
182250062 48261836 A 2 3006104
So I want to find used helper codes for T151 which is IsPrincipal equals 1.
Output should like this:
Code Helper_I_Count Helper_A_Count
T151 3 1
So I tried Like this:
SELECT t.`Code`,COUNT(v1.`Code`) AS EkTaniSay,COUNT(v2.`Code`) AS IslemSay
FROM TIGPatientCode t,
(
SELECT DRGPatientId,`Code`
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='I'
) v1,
(
SELECT DRGPatientId,`Code`
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='A'
) v2
WHERE t.IsPrincipal='1' AND t.DRGPatientId=v1.DRGPatientId AND t.DRGPatientId=v2.DRGPatientId
GROUP BY t.`Code`
But it wont get actual count.
How can I Do this?
Thanks
SELECT t2.Code,
SUM(t1.CodeType = 'I') AS EkTaniSay,
SUM(t1.CodeType = 'A') AS IslemSay
FROM TIGPatientCode AS t1
RIGHT JOIN TIGPatientCode AS t2 ON t1.DrgPatientId = t2.DrgPatientId
WHERE t1.isPrincipal != 1 AND t2.isPrincipal = 1
GROUP BY t1.DrgPatientId;
The first part of the query is based on multiple query same table but in different columns mysql. Then I join this with the table again to get the code for the principal row.
The problem with your query is that joining the two subqueries creates a cross-product of all the rows, which causes the counts to be multiplied. Also, if there's any group that doesn't have one of the codes, that subquery will return no rows, so the join will be empty for that code. You could fix the first problem by doing the counts in the subqueries rather than the main query. The second problem can be fixed by using LEFT JOIN. So the fixed version of your query would look like:
SELECT t.Code, v1.EkTaniSay, v2.IslemSay
FROM TIGPatientCode t
LEFT JOIN (
SELECT DRGPatientId, COUNT(*) AS EkTaniSay
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='I'
GROUP BY DRGPatientId
) AS v2 ON t.DRGPatientId = v2.DRGPatientId
LEFT JOIN (
SELECT DRGPatientId, COUNT(*) AS IslemSay
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='A'
GROUP BY DRGPatientId
) AS v1 ON t.DRGPatientId = v1.DRGPatientId
WHERE t.IsPrincipal = 1
DEMO

Mysql query optimization issue

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;

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.

MySQL - Using column value for joining in the same query

I have three tables that looks something like this:
Table joins
|ID|JOIN_NAME|
1 persons
2 companies
Table information
|ID|JOIN_ID|
1 1
2 2
Table information_extra_persons
|ID|INFORMATION_ID|NAME|
1 1 John
Table information_extra_companies
|ID|INFORMATION_ID|NAME|
1 2 IBM
How can i join together these tables in one SQL? I've tried something like:
SELECT * FROM `information`
INNER JOIN `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`
ON `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`.`information_id` = `information`.`id`
but I can't get it to work. Of course this isn't my actual table setup, but it's the same principle. Does anyone know how to get all the info in just one SQL?
That's actually four tables, not three. This isn't just a nitpick - it looks as though the substance of your question is "how can I use the name of the table as part of the join criteria?" (ie. how can the information_extra_ tables be treated as a single table?)
To which the answer is: you can't. (Outside of dynamic SQL.)
In this specific case, the following should return what I think you are looking for:
select j.join_name joined_entity,
case when j.join_name = 'persons' then p.name
else c.name
end joined_entity_name
from information i
inner join joins j on i.join_id = j.id
left join information_extra_persons p on i.id = p.information_id
left join information_extra_companies c on i.id = c.information_id
Alternatively, a less efficient (but more general) approach might be:
select j.join_name joined_entity,
v.name joined_entity_name
from information i
inner join joins j on i.join_id = j.id
inner join (select 'persons' entity, information_id, name from information_extra_persons
union all
select 'companies' entity, information_id, name from information_extra_companies) v
on i.id = v.information_id and j.join_name = v.entity