IMPALA SQL (pivoting table) - sql

Here I’ve got the IMPALA SQL task, can someone help me to solve it please
There is a table with three columns A B and C.
There are three types of values: child in column A; child/parent in column A and C; and owner in column B.
IF value appears in column C, then it is a parent value for other value in the same row in the column A, and it also lives in column A a swell (as a child).
There are multiple levels of children and parents (child for one value can be parent for another).
All children and parent values are live together in column A;
Owners are live only in column B;
Children parent chain ends when owner appears (in column B).
I need to pivot existing table in a way that reveals every chain in a separate row
Child, parent-child 1, parent-child 2, parent-child 3, Owner.
see pic_1
I tried to solve this problem with LEFT JOINS
SELECT a.A, a.B, a.C, b.A, b.B, b.C, c.A, c.B, c.C
FROM
table1 a
LEFT JOIN
table1 b
ON a.C = b.A
LEFT JOIN
table1 c
ON b.C = c.A
it works but it is a bad solution because we don’t know how many levels of ‘parent-child’ can be there. some chains are consist of only three parent/child
values and some of them have 50 'chain-rings' prior to the owner

Related

Problem with JOINs on selection of related data

I have three entity types (let's call them A, B, C) persisted on three tables of my database.
Each entity type has a relation with the other two entities. Relations are persisted in three tables of the DB as well (let's call them AB, AC, BC), where every record is a couple of IDs of the respective entities.
Relations A-B are one-to-many and are mandatory: every A has at least one relation with a B, every B has a relation with an A.
Relations A-C and B-C are many-to-many and are optional: there can be As without relation with Cs, there can be Bs without relations with Cs.
I cannot change this schema.
I need to build a table with all the As and their related data. Every row of the table must contain only related data or NULLs where there are no relations.
I thought I would be fine with something like:
SELECT * -- let's omit columns for simplicity
FROM AB
LEFT JOIN BC ON BC.IdB = AB.IdB
LEFT JOIN AC ON AC.IdA = AB.IdA AND
AC.IdC = BC.IdC
INNER JOIN A ON A.Id = AB.IdA
INNER JOIN B ON B.Id = AB.IdB
LEFT JOIN C ON C.Id = AC.IdC
and then filtering with a WHERE clause. My problem is I don't get how, which makes me think I am approaching the problem in the wrong way.
Any hint would be appreciated, thank you in advance.

How to create a query that returns only records in one table without a foreign key

How do I create a query that returns only records in one table that have no foreign key relations with a particular attribute? I'm not even sure how to properly formulate the question, so I'm giving an example below:
I have Table B. It has an ID and other stuff that's not important.
I have Table C. It has an ID, an attribute (call it Available) which is only ever 'Yes' or 'No'.
Each record in Table B may have zero or more records in Table C related to it. They are connected by Table BC_Line, where each record has a FK for B_ID and a FK for C_ID. Hence, if records C_ID=1 and C_ID=2 are related to B_ID=1, then BC_Line has two records: (B_ID_FK=1 C_ID_FK=1) and (B_ID_FK=1 C_ID_FK=2).
I want a query that returns ONLY records in B that have NO associated records in C with C_Available='No'. A record in B might have several related records in C, all with 'Yes', and that would be shown. A record in B might have several related records in C all with 'No', that would NOT be shown. A record in B might also have records in C, some 'Yes' and some 'No', but that record would still not be shown. All related C records must be 'Yes'.
I'm not sure how to do it. I understand how to create queries and how to do Joins, but I do not know how to combine them in such a way as to get what I want. It's possible this is a well known problem, but I haven't been able to find the answer, partly because I have difficulty articulating my problem.
For the record, I am using Oracle.
You could use EXISTS:
SELECT *
FROM TableB b
WHERE NOT EXISTS (SELECT 1
FROM TableC c
WHERE c.C_Available='No'
AND b.B_ID_FK =c.C_ID);
Note! I've assumed that c.C_Available is defined as NOT NULL and it could hold only Yes/No values.
You could also use the left join method.
select b.*
from tableB b left join bc_line on b.b_id = bc.b_id
left join tableC c on bc.c_id = c.c_id
and c.c_available = 'No'
where c.c_id is null

Joining tables which don't share a field, but have a unique ID that is shared

Beginner SQLmancer looking for a hand with something.
I have two tables I am trying to join but they don't share a column [tables A & B]. There is a table [C] which has a unique field that can be joined to both [Field 1 for Table A, 2 for Table B] and these two fields have a 1-to-1 relationship [For each value in Field 1, it has a corresponding unique value in Field 2 in Table C].
Is there a way for me to join table A & B "through" table C in a way that I can see data from just A&B? Or, can I join them each to C to do this? If so, what kind of join is needed?
Any tips would be appreciated!
You basically join all 3. This is very common way to associate records in databases, table C is often called a "Join table"
SELECT
A.*, b.*
FROM
A, B, C
WHERE
A.foo = C.foo
AND B.bar = C.bar

Joining, but not joining (hypothetical q.)

Lets suppose that I have a table A with couple of columns. I work with tables, where there is no index on the entries, since they are 'historical' tables. I use one specific column, though, to sort of identify my things. Lets call this ID.
If you'd make a query like the one below, sometimes you'd get one line back, other cases a few.
SELECT * FROM A WHERE ID = '<something>'
Lets say I have two more tables, B and C. Both have ID columns, like A.
Also, some of the IDs in A, are also in B OR C. IDs existing in B CANNOT exist in C. And ALL IDs in A EXIST in either B OR C.
B and C contain extra information, which I'd like to join to A at the same SELECT.
My problem is, that they would only provide extra information. I do not want extra lines in my output..
To make it more clear: my selection from A returns a hundred lines of output.
When I left/right/inner join B table, I —probably— will have less lines as output. Same thing with joining C.
AND FINALLY, my question is:
Is there a way to join table B only on those IDs, which exist in B and vice versa? (And it I would want it in the same SELECT.... statement.)
If you don't want extra lines in your output, you could do something like this:
select *
from A join
(select B.*
from B
group by B.id
) B
on A.id = B.id;
This would choose arbitrary values from B for each id and join them back to A. Is this what you want?
Well it seems like you should build some left join between A and two "Select MAX"s: one from table B, the other one from table C.
And if you do not want 'duplicate' IDs from table A, a 'group by' on table A should help.

SQL mapping between multiple tables

This is a SQL design question. First, the setup. I have three tables:
A, which is automatically populated based on a query against a linked server. The data in this table cannot be changed;
B, which has just a dozen or so rows, containing the names for collections of As;
AtoB, which is the mapping table by which As are organized into named collections, with foreign keys on both columns;
For example, A contains:
Giraffe
Owl
Tiger
And B contains:
Seattle Zoo
San Jose Zoo
And AtoB contains:
1,1 (Giraffe in Seattle)
2,1 (Owl in Seattle)
3,1 (Tiger in Seattle)
2,2 (Owl in San Jose)
Now, the problem:
I've been asked to include in some of these collections items not found in A. So, I create a table, C, with the same identity and Name columns as A, and populate it. In keeping with the earlier example, let's say C contains:
Dragon
The question is, how do I include items from C in AtoB? What if I need to include a Dragon in the Seattle Zoo?
My first instinct, being naive, was to create a view V containing the union of A and C, and modifying AtoB to be VtoB. That's where my naivety paid off: one cannot create a foreign key to a view.
I suspect that there's a standard, correct means of relating one or more A OR C with a B.
To expand on Arthur Thomas's solution here's a union without the WHERE in the subselects so that you can create a universal view:
SELECT A.Name as Animal, B.Name as Zoo FROM A, AtoB, B
WHERE AtoB.A_ID = A.ID && B.ID = AtoB.B_ID
UNION
SELECT C.Name as Animal, B.Name as Zoo FROM C, CtoB, B
WHERE CtoB.C_ID = C.ID && B.ID = CtoB.B_ID
Then, you can perform a query like:
SELECT Animal FROM zoo_animals WHERE Zoo="Seattle Zoo"
If you can't put a Dragon in A then you will need to create another table and another link table. The problem is creating a unique set of data that needs to be stored (another table) that cannot be the same set as A. Since it isn't the same set then you can no longer use the link table (AtoB) which has foreign keys that ensure that the link is a reference from set A. So you could create a tables like this:
imaginary_creatures
id
name
imaginary_creatures_to_b
imaginary_creatures_id (link to imaginary_creatures table)
b_id (link to zoos table)
Later when you want to get all creatures in a zoo you can do a UNION
SELECT A.Name FROM A where A.ID IN
(SELECT AB.A_ID FROM AtoB AB WHERE B_ID =
(SELECT B.ID FROM B WHERE B.Name = 'Zoo Name'))
UNION
SELECT i.name FROM imaginary_creatures i i.id IN
(SELECT ic.imaginary_creatures_id FROM imaginary_creatures_to_c ic
WHERE ic.b_id = (SELECT B.ID FROM B WHERE B.Name = 'Zoo Name'))
There may be a better way of writing that, but it should work for your purposes.
Arthur Thomas has a good solution, the other possible solution is to add a column to the link table indicating which table (A or C) it is related to. Then enforce the relationships through triggers rather than foreign keys. But really Arthur's solution is the preferred way of doing this sort of thing.
What you want to do is put Dragon in A, and if you want to select ALL records from A regardless of if they have a matching record in AtoB, do a LEFT OUTER JOIN. Something like this:
SELECT * FROM A
LEFT OUTER JOIN AtoB
ON A.id = AtoB.A_ID
Edit: This would only work if you could add your new records to A. I missed the fact that you are not able to. I think Arthur Thomas's solution is what you want.
Truncate the table dept_details
Display the structure of the table emp_details
Convert the first letter of emp_name into capitals.