How to left join or inner join a table itself - mysql

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.

Related

Contiditional WHERE with two joins on same table

I'm trying to create an effective query but can't get it working.
Tables:
- one table containing types of objects
- one table containing objects
Conditions:
- there can be single objects of a type
- there can be child objects of a type
- parent and child objects don't need to be of the same type
- objects can be published
- types can be published
- the results should only get pulled from a specific pool of object IDs. So i need to add AND (o.id IN (1,2,3,4)
I want a simple result list that shows how many types are published and the number of objects assigned to these types.
types
id | title | published
---------------------
1 type1 1
2 type2 1
3 type3 1
4 type4 1
5 type5 1
6 type6 0
7 type7 1
objects
id |title | type | parent | published
---------------------------------------
1 a 1 0 1
2 b 1 0 1
3 c 3 2 1
4 d 2 0 1
5 e 2 2 1
6 f 4 0 0
7 g 5 6 1
8 h 6 0 1
9 i 3 8 1
10 j 3 8 0
11 k 7 8 1
Results should be:
type1 (#2) (two singles)
type2 (#2) (one single + one child of id 2)
type3 (#3) (one child of id 2 + one published child of id 8)
type4 (#0) (one single not published)
type5 (#0) (because it's parent id 6 is not published)
type6 (#0) (because type6 is not published)
I tried this one (type publishing not included):
SELECT o.type, t.title, COUNT(t.id) AS cnt
FROM types AS t
LEFT JOIN objects AS o ON o.type = t.id
LEFT JOIN objects AS o2 ON o.id = o2.parent
WHERE o.published = 1 AND o2.published = 1
GROUP BY o.type
The conditions in the WHERE clause negate the "outerness" of the left joins.
Move those conditions to the ON clauses. The WHERE clause can be dropped.
Also, reference columns from t, the driving table, and count non-NULL expressions from the outer joined tables.
That will allow the query to return zero counts.
I didn't fully delve into the specification, but it looks like we want to count matching rows from o and o2.
I think something like this will get a resultset consistent with one interpretation of the specification... child o2 rows get counted under parent o type, regardless of the type on the child o2 row.
This is not tested, and I'm not fully understanding the specification...
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(DISTINCT o.id)
+ COUNT(DISTINCT o2.id) AS `cnt`
-- , COUNT(DISTINCT o.id) AS `cnt_o`
-- , COUNT(DISTINCT o2.id) AS `cnt_o2`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
LEFT
JOIN objects o2
ON o2.parent = o.id
AND o2.published = 1
GROUP
BY t.id
, t.title
Not clear in the spec...
Do child rows (from o2) get omitted from the count if the type on the o2 row matches a row in types that is published=0 ?
If we are "grouping" by type on the o2 rows , then we'd need to something different,
EDIT
we could get the count from the parent and the child separately, in two separate SELECT, and then combine the two resultsets with a UNION ALL set operator, and then total up the counts.
something along these lines:
SELECT c.type
, c.title
, SUM(c.cnt) AS cnt
FROM (
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
GROUP
BY t.id
, t.title
UNION ALL
SELECT tc.id AS `type`
, tc.title AS `title`
, COUNT(oc.id) AS `cnt`
FROM types tc
JOIN objects oc
ON oc.type = t.id
AND oc.published = 1
AND t.published = 1
JOIN objects op
ON op.id = oc.parent
AND op.published = 1
JOIN types pt
ON pt.id = op.type
AND pt.published = 1
GROUP
BY tc.id
, tc.title
) c
GROUP
BY c.type
, c.title
again, untested, and without a full understanding of the spec.
the count of the parent o is straightforward. we use an outer join, with t as the driving table, so we get all types, and can get zero counts.
the count of the child oc, we can do inner joins. since the previous SELECT is getting us all the types, missing rows in the second SELECT won't cause a problem.
note that we join the child o2 rows by type, and then we join to parent (to make sure parent is published), and join to parent type (to check that type is published) ...
How do we distinguish "parent" rows, do we check parent=0 ?
Is this a hierarchy, can a "child" also be the "parent" of another row ?
FOLLOWUP
Another way to think about it (maybe this was the approach of the OP query) ... we are counting rows from o, parents and children. What's important is that the type is published type, and that o is published.
Additionally, either
o is not a child (i.e. there isn't a row in objects op that has an id value equal to `o.parent)
or
if o does have a parent row (a row in objects op with an id value equal to o.parent, the [parent op is published and the parent type is published.
We could approach it like this:
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND t.published = 1
LEFT
JOIN objects op
ON op.id = o.parent
LEFT
JOIN types pt
ON pt.id = op.type
WHERE -- this not a child (there is no parent)
op.id IS NULL
OR -- parent is published and parent type is published
( op.published = 1 AND pt.published = 1 )
GROUP
BY t.id
, t.title

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 ;

An explanation with SQL query

I trying to get some data for my JavaFX Application from a couple of tables in database with MySQl.
Here's the query:
select veturattable.id, veturattable.vetura,veturattable.modeli,veturattable.ngjyra,
veturattable.targa, renttable.pagesa, hargjimettable.shuma
from veturattable
left join hargjimettable
on hargjimettable.veturaid= veturattable.id
left join renttable
on renttable.veturaid = veturattable.id ;
Here are datas from rentable
And here are datas from hargjimettable
So what I need is to show me this one:
veturaid | pagesa | shuma
1 | 150 | 91
10 | 110 | 40
You actually need to do two subqueries pre-aggregating the sum amounts per respective ID. Then join each individually back to the main. If you don't, you are getting a Cartesian product. For every record in the hargjimettable table for a given ID, it is joined to the renttable for each amount there. So, if you have 2 records in first table and 3 records in the second, you are getting a multiple of 6.
By pre-querying each grouping by the one ID key respectively, you will only have at most, one record for each possible summation. So grab that record if it exists. The left-join prevents some IDs from not showing up. Using coalesce() prevents nulls from showing.
select
v.id,
v.vetura,
v.modeli,
v.ngjyra,
v.targa,
COALESCE( RSum.SumPagesa, 0 ) as AllPagesa,
COALESCE( HSum.SumShuma, 0 ) as AllShuma
from
veturattable v
left join
( select
h.veturaid,
SUM( h.shuma ) as SumShuma
from
hargjimettable h
group by
h.veturaid ) HSum
ON v.id = HSum.veturaid
left join
( select
r.veturaid,
SUM( r.pagesa ) as SumPagesa
from
renttable r
group by
r.veturaid ) RSum
ON v.id = RSum.veturaid
You actually want the MAX() and SUM() along the GROUP BY like
select max(veturattable.id) as id, max(veturattable.vetura) as vetura,
max(veturattable.modeli) as modeli,
max(veturattable.ngjyra) as ngjyra,
max(veturattable.targa) as targa,
max(renttable.pagesa) as pagesa,
sum(hargjimettable.shuma) as shuma
from veturattable
left join hargjimettable
on hargjimettable.veturaid= veturattable.id
left join renttable
on renttable.veturaid = veturattable.id
group by veturattable.id;

JOIN two tables while grouping by an index and maching for multiple foreign ids

I have the 3 Following Tables:
objects:
ObjectID ObjectDescription
1 "first"
2 "second"
attributes:
AttributeID AttributeDescription
1 "att1"
2 "att2"
3 "att3"
4 "att4"
attributelink:
AttributeID ObjectID
1 1
2 1
4 1
Now my question: I want now have some attributes selected and want to know, which Object has all my selected Attributes. I've tried the following:
SELECT * FROM `objects`
INNER JOIN `attributelink`
ON `objects`.`ObjectID` = `attributelink`.`ObjectID`
WHERE `attributelink`.`AttributeID` =1 AND `attributelink`.`AttributeID` =2
GROUP BY `objects`.`ObjectID`
That obviously doesn't work, because one row can't have 2 AttributeIDs, but how can I archieve this?
You have to join on the attributelink table once for each selected attribute you want to check for:
SELECT o.ObjectID
FROM objects o
INNER JOIN attributelink a1 ON o.ObjectID = a1.ObjectID AND a1.AttributeID = 1
INNER JOIN attributelink a2 ON o.ObjectID = a2.ObjectID AND a2.AttributeID = 2
GROUP BY o.ObjectID
Your test data doesn't show a whole lot about whether it works or not, but FWIW, here's the sqlfiddle.
Another way to do it is to use COUNT DISTINCT with HAVING and GROUP BY (sqlfiddle):
SELECT o.ObjectID
FROM objects o
INNER JOIN attributelink a ON o.ObjectID = a.ObjectID
WHERE a.AttributeID IN (1,2) --here you filter the rows on the attributes to test
GROUP BY o.ObjectID
HAVING COUNT(DISTINCT(a.AttributeID)) = 2 --# of attributes, means "having ALL"

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