Oracle PL/SQL: referencing to a column name in inner joined query - sql

I have the following SQL statement:
SELECT *
FROM cars car
LEFT JOIN (SELECT *
FROM cars auto
LEFT JOIN steeringwheels sw
ON auto.steeringwheelid = sw.ID
WHERE material = 'leather') innertable
ON innertable.ID = car.ID
LEFT JOIN steeringwheels sw
ON auto.steeringwheelid = sw.ID
WHERE sw.material='plastic'
This query delivers the columns from the table "Cars" twice, but with different values for the ID from the Car table (the purpose of the query is to map the values to see what the Car.ID would be id the material would change from leather to plastic).
------------------------------------
| ID | material | ID_1 | material_1 |
-------------------------------------
| 1 | leather | 4 | plastic |
| 2 | leather | 7 | plastic |
-------------------------------------
However, I would like to output only the ID columns (not the material columns), like this:
-------------
| ID | ID_1 |
-------------
| 1 | 4 |
| 2 | 7 |
-------------
I have not been able to do this, as I haven't found a way to refer to the ID column of the inner query in any way. For example
SELECT id, innertable.id
(...)
or
SELECT id, auto.id
(...)
or
SELECT id, id_1
(...)
don't seem to work. How can achieve this?

Try explicitly listing the column names of the inner table in the select statement. Like:
...(SELECT auto.ID autoid, auto.Whatever)....
and then in the main select:
SELECT innertable.autoid ....

Is this what you're after?
SELECT auto_id, steeringwheel_id
FROM cars car
LEFT JOIN (SELECT auto.ID AS auto_id, sw1.id AS steeringwheel_id
FROM cars auto
LEFT JOIN steeringwheels sw1
ON auto.steeringwheelid = sw1.ID
WHERE material = 'leather') innertable
ON innertable.auto_ID = car.ID
LEFT JOIN steeringwheels sw2
ON auto.steeringwheelid = sw2.ID
WHERE sw.material='plastic'

Related

Multiple select from CTE with different number of rows in a StoredProcedure

How to do two select with joins from the cte's which returns total number of columns in the two selects?
I tried doing union but that appends to the same list and there is no way to differentiate for further use.
WITH campus AS
(SELECT DISTINCT CampusName, DistrictName
FROM dbo.file
),creditAcceptance AS
(SELECT CampusName, EligibilityStatusFinal, CollegeCreditAcceptedFinal, COUNT(id) AS N
FROM dbo.file
WHERE (EligibilityStatusFinal LIKE 'Eligible%') AND (CollegeCreditEarnedFinal = 'Yes') AND (CollegeCreditAcceptedFinal = 'Yes')
GROUP BY CampusName, EligibilityStatusFinal, CollegeCreditAcceptedFinal
),eligibility AS
(SELECT CampusName, EligibilityStatusFinal, COUNT(id) AS N, CollegeCreditAcceptedFinal
FROM dbo.file
WHERE (EligibilityStatusFinal LIKE 'Eligible%')
GROUP BY CampusName, EligibilityStatusFinal, CollegeCreditAcceptedFinal
)
SELECT a.CampusName, c.[EligibilityStatusFinal], SUM(c.N) AS creditacceptCount
FROM campus as a FULL OUTER JOIN creditAcceptance as c ON a.CampusName=c.CampusName
WHERE (a.DistrictName = 'xy')
group by a.CampusName ,c.EligibilityStatusFinal
Union ALL
SELECT a.CampusName , b.[EligibilityStatusFinal], SUM(b.N) AS eligible
From Campus as a FULL OUTER JOIN eligibility as b ON a.CampusName = b.CampusName
WHERE (a.DistrictName = 'xy')
group by a.CampusName,b.EligibilityStatusFinal
Expected output:
+------------+------------------------+--------------------+
| CampusName | EligibilityStatusFinal | creditacceptCount |
+------------+------------------------+--------------------+
| M | G | 1 |
| E | NULL | NULL |
| A | G | 4 |
| B | G | 8 |
+------------+------------------------+--------------------+
+------------+------------------------+----------+
| CampusName | EligibilityStatusFinal | eligible |
+------------+------------------------+----------+
| A | G | 8 |
| C | G | 9 |
| A | T | 9 |
+------------+------------------------+----------+
As you can see here CTEs can be used in a single statement only, so you can't get the expected output with CTEs.
Here is an excerpt from Microsoft docs:
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE
statement that references some or all the CTE columns. A CTE can also
be specified in a CREATE VIEW statement as part of the defining SELECT
statement of the view.
You can use table variables (declare #campus table(...)) or temp tables (create table #campus (...)) instead.

Count how many times a value appears in tables SQL

Here's the situation:
So, in my database, a person is "responsible" for job X and "linked" to job Y. What I want is a query that returns: name of person, his ID and he number of jobs it's linked/responsible. So far I got this:
select id_job, count(id_job) number_jobs
from
(
select responsible.id
from responsible
union all
select linked.id
from linked
GROUP BY id
) id_job
GROUP BY id_job
And it returns a table with id in the first column and number of occurrences in the second. Now, what I can't do is associate the name of person to the table. When i put that in the "select" from beginning it gives me all the possible combinations... How can I solve this? Thanks in advance!
Example data and desirable output:
| Person |
id | name
1 | John
2 | Francis
3 | Chuck
4 | Anthony
| Responsible |
process_no | id
100 | 2
200 | 2
300 | 1
400 | 4
| Linked |
process_no | id
101 | 4
201 | 1
301 | 1
401 | 2
OUTPUT:
| OUTPUT |
id | name | number_jobs
1 | John | 3
2 | Francis | 3
3 | Chuck | 0
4 | Anthony | 2
Try this way
select prs.id, prs.name, count(*) from Person prs
join(select process_no, id
from Responsible res
Union all
select process_no, id
from Linked lin ) a on a.id=prs.id
group by prs.id, prs.name
I would recommend aggregating each of the tables by the person and then joining the results back to the person table:
select p.*, coalesce(r.cnt, 0) + coalesce(l.cnt, 0) as numjobs
from person p left join
(select id, count(*) as cnt
from responsible
group by id
) r
on r.id = p.id left join
(select id, count(*) as cnt
from linked
group by id
) l
on l.id = p.id;
select id, name, count(process_no) FROM (
select pr.id, pr.name, res.process_no from Person pr
LEFT JOIN Responsible res on pr.id = res.id
UNION
select pr.id, pr.name, lin.process_no from Person pr
LEFT JOIN Linked lin on pr.id = lin.id) src
group by id, name
order by id
Query ain't tested, give it a shot, but this is the way you want to go

SQL Server - OR clause in join confusion

I have the table structure like below
Package
PACK_ID | DESCR | BRAND_ID
1 | Shoes | 20
2 | Cloths| NULL
ITEMS
ITEM_ID | PACK_ID | BRAND_ID
100 | 1 | 10
101 | 1 | NULL
102 | 1 | 10
BRANDS
NAME | BRAND_ID
A | 10
B | 20
I want to write a query to list how many items are there in a package grouped by same brand. If the brand is not defined in the item it should get it from package.
Note: Brand_id in both package and items are nullable
My query is this
SELECT count (*) as count,p.descr as descr,b.name FROM [items] item
inner join [package] p on item.pack_id= p.pack_id
inner join [brands] b on b.brand_id = item.brand_id or b.brand_id = p.brand_id
where p.pack_id = 1
group by b.name,p.descr
and my result is
COUNT | descr | NAME
2 | Shoes | a
3 | Shoes | B
whereas i expect the result to be something like this
COUNT | descr | NAME
2 | Shoes | a
1 | Shoes | B
could you please suggest what is wrong with my code? Thanks in advance.
Try using ISNULL on your join condition:
SELECT count (*) as count,p.pack_id as pack_id,b.name FROM [items] item
inner join [package] p on item.pack_id= p.pack_id
inner join [brands] b on b.brand_id = ISNULL(item.brand_id, p.brand_id)
where p.pack_id = 1
group by b.name,p.pack_id
Your OR was causing it to join to multiple rows, this should use the item by default and then fall back to the package.
I would tend to approach this by getting the brand for both the item and the package. Then decide which one to use in the select:
SELECT count(*) as count, p.descr as descr, coalesce(bi.name, bp.name) as name
FROM [items] item inner join
[package] p
on item.pack_id= p.pack_id left join
[brands] bi
on bi.brand_id = item.brand_id left join
brands bp
on b.brand_id = p.brand_id
where p.pack_id = 1
group by coalesce(bi.name, bp.name), p.descr;
One key advantage to this approach is performance. Databases tend to do a poor job when joins are on expression or or conditions.

SQL - Compare 2 tables then add unique items to 3rd table

Title kind of says it all.
I have 2 tables Both with matching data
Table Name: Customers
_____________________________
ID | CompanyName
--------------
1 | Joes
2 | Kennys
3 | Kellys
4 | Ricks
5 | Johns
Table Name: OldCustomers
_____________________________
ID | CompanyName
--------------
1 | Joes
2 | Kennys
3 | Kellys
4 | Ricks
I want to do a comparison between the two tables. Then take the Row that doesn't exist in Table 2, and add it to a table i've created called "NewCustomers"
You can use the following:
insert into NewCustomers(id, companyname)
select c.id
, c.companyname
from Customers c
left join OldCustomers oc
on c.companyname = oc.companyname
where oc.id is null;
select *
from NewCustomers
See SQL Fiddle with Demo
This query will find all records where the company name is missing from the OldCustomers table. If you want to join on both id and company name you would just add and c.id = oc.id to the left join. It will give you the same results.

Remove NULL values from multiple LEFT JOIN in sql server

I have the following tables
ITEM1
ID | NAME | GEARS | ITEM2_ID |
-------------------------------
1 | Test | 56 | 4 |
2 | Test2| 12 | 2 |
ITEM3
ID | NAME | DATA | ITEM2_ID |
-------------------------------
1 | Test | 1 | 1 |
2 | Test7| 22 | 3 |
ITEM2
ID | VALUE |
--------------------
1 | is simple |
2 | is hard |
3 | is different|
4 | is good |
5 | very good |
And my query
SELECT TOP(3) * FROM (
SELECT ID,
rankTable.RANK as RANK_,
TOTALROWS = COUNT(*) OVER()
FROM ITEM2
INNER JOIN
CONTAINSTABLE(ITEM2, [VALUE], 'ISABOUT("good")') as rankTable
ON ITEM2.ID = rankTable.[KEY]
) as ITEM2table
LEFT JOIN (
SELECT ID,
NAME,
GEARS,
ITEM2_ID
FROM ITEM1
) as ITEM1table
ON ITEM1table.ITEM2_ID = ITEM2table.ID
LEFT JOIN (
SELECT ID,
NAME,
DATA,
ITEM2_ID
FROM ITEM3
) as ITEM3table
ON ITEM3table.ITEM2_ID = ITEM2table.ID
and the results
How to remove (if is possible) the first row (ID = 5) using the above SQL query ? Also I want to show TOTALROWS = 1 because other row contains NULL's except first 3 columns.
Thank you.
If I understand correctly, you want to keep only the rows where either the first or the second (or both) outer join succeeds:
WHERE ITEM1table.ITEM2_ID IS NOT NULL
OR ITEM3table.ITEM2_ID IS NOT NULL
Some simplifications can be done on the query. No need for the nested subqueries:
SELECT TOP(3)
ITEM2table.ID,
rankTable.RANK as RANK_,
TOTALROWS = COUNT(*) OVER(),
ITEM1table.*,
ITEM3table.*
FROM
ITEM2
INNER JOIN
CONTAINSTABLE(ITEM2, [VALUE], 'ISABOUT("good")') as rankTable
ON ITEM2.ID = rankTable.[KEY]
LEFT JOIN
ITEM1 as ITEM1table
ON ITEM1table.ITEM2_ID = ITEM2.ID
LEFT JOIN
ITEM3 as ITEM3table
ON ITEM3table.ITEM2_ID = ITEM2.ID
WHERE ITEM1table.ITEM2_ID IS NOT NULL
OR ITEM3table.ITEM2_ID IS NOT NULL
ORDER BY something --- you need to order by something
--- if you use TOP. Unless you want
--- 3 (random) rows.
Maybe there's an obvious reason, but if you want to eliminate rows where the second table doesn't have a match, why are you using a left join? It seems like your first join should be an inner join and your second should be left - that would give you the results you want in this case.
You can either use INNER JOIN instead of LEFT JOIN, or put
WHERE ITEM1table.ID IS NOT NULL AND ITEM3table.ID IS NOT NULL
at the end of your query