Why is SQL not letting me access information in inner queries? - sql

I'm writing a query to find solve the following question:
"For those customer – product combinations where the product belongs to one of the product lines that have ‘Ethernet’ in their name, list the name of the customer, name of the product, the sales last year and total sales year to date."
Now, I have four tables that I need to use to solve this: xproduct, xprodline, and xsales, and xcustomer. These are related in the following ways:
And they have the following columns:
Since xcustomer and xproduct are not directly related, I'm using xsales to join them, but I'm having issues accessing the information I get from inner queries. This is the code I have so far, but it throws "ORA-00904: "S"."SALES_YEAR_TO_DATE": invalid identifier":
SELECT xcustomer.cust_name, PL.prod_name, S.sales_last_year, S.sales_year_to_date FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;

Try this:
SELECT xcustomer.cust_name, PL.prod_name, S.sales_last_year, S.sales_year_to_date FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr, xsales.sales_last_year, xsales.sales_year_to_date FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;
You were missing selecting the columns you needed.

You are missing one more column (PROD_NAME) in your subquery. Here is the updated query:
SELECT xcustomer.cust_name,
S.prod_name,
S.sales_last_year,
S.sales_year_to_date
FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr, xsales.sales_last_year, xsales.sales_year_to_date, pl.prod_name
FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr
FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;
Also, I think you can directly join the tables and simplify your query as follows:
SELECT C.cust_name,
PL.prod_name,
S.sales_last_year,
S.sales_year_to_date
FROM xcustomer C
JOIN xsales S
ON C.cust_nbr = S.sales_cust_nbr
JOIN xprodline PL
ON S.sales_prod_nbr = PL.prod_nbr
JOIN xproduct P
ON PL.prodline_nbr = P.prod_prodline
WHERE P.prod_prodline= 1

Related

LEFT JOIN not keeping only records that occur in a SELECT query

I have the following SQL select statement that I use to get a subset of products, or wines:
SELECT pv.SkProdVariantId AS id,
pa.Colour AS colour,
FROM Dim.ProductVariant AS pv
JOIN ProductAttributes_new AS pa
ON pv.SkProdVariantId = pa.SkProdVariantId
WHERE pv.ProdTypeName = 'Wines'
The length of this table generated is 3,905. I want to get all the transactional data for these products.
At the moment I'm using this select statement
SELECT c.CalDate AS timestamp,
f.SkProductVariantId AS sku_id,
f.Quantity AS quantity
FROM fact.FTransactions AS f
LEFT JOIN Dim.Calendar AS c
ON f.SkDateId = c.SkDateId
LEFT JOIN (
SELECT pv.SkProdVariantId AS id,
pa.Colour AS colour,
FROM Dim.ProductVariant AS pv
JOIN ProductAttributes_new AS pa
ON pv.SkProdVariantId = pa.SkProdVariantId
WHERE pv.ProdTypeName = 'Wines'
) AS s
ON s.id = f.SkProductVariantId
WHERE c.CalDate LIKE '%2019%'
The calendar dates are correct, but the number of unique products returned is 5,648, rather than the expected 3,905 from the select query.
Why does my LEFT JOIN on the first select query not work as I expect it to, please?
Thanks for any help!
If you want all the rows form your query, it needs to be the first reference in the LEFT JOIN. Then, I am guessing that you want transaction in 2019:
select . . .
from (SELECT pv.SkProdVariantId AS id, pa.Colour AS colour,
FROM Dim.ProductVariant pv JOIN
ProductAttributes_new pa
ON pv.SkProdVariantId = pa.SkProdVariantId
WHERE pv.ProdTypeName = 'Wines'
) s LEFT JOIN
(fact.FTransactions f JOIN
Dim.Calendar c
ON f.SkDateId = c.SkDateId AND
c.CalDate >= '2019-01-01' AND
c.CalDate < '2020-01-01'
)
ON s.id = f.SkProductVariantId;
Note that this assumes that CalDate is really a date and not a string. LIKE should only be used on strings.
You misunderstand somehow how outer joins work. See Gordon's answer and my request comment on that.
As to the task: It seems you want to select transactions of 2019, but you want to restrict your results to wine products. We typically restrict query results in the WHERE clause. You can use IN or EXISTS for that.
SELECT
c.CalDate AS timestamp,
f.SkProductVariantId AS sku_id,
f.Quantity AS quantity
FROM fact.FTransactions AS f
INNER JOIN Dim.Calendar AS c ON f.SkDateId = c.SkDateId
WHERE DATEPART(YEAR, c.CalDate) = 2019
AND f.SkProductVariantId IN
(
SELECT pv.SkProdVariantId
FROM Dim.ProductVariant AS pv
WHERE pv.ProdTypeName = 'Wines'
);
(I've removed the join to ProductAttributes_new, because it doesn't seem to play any part in this query.)

Oracle SQL - how to NOT SHOW athlete name that apears only once

created a view called winners, it contains the columns: athlete_name,year,medal_won
its basicly athletes that won olympic medal and the year,
it look like that,
data base is in live sql: https://livesql.oracle.com/apex/f?p=590:1000:0
select distinct year,athlete_name,medal
from olym.olym_medals
join olym.olym_athlete_games on olym_athlete_games.id = olym_medals.athlete_game_id
join olym.olym_nations on olym_nations.id = olym_athlete_games.nation_id
join olym.olym_games on olym_games.id = Olym_athlete_games.game_id
join olym.olym_athletes on olym_athletes.id = olym_athlete_games.athlete_id
order by athlete_name
as you can see some name show only once and some names are showing more than once, i want to get rid off all lines of those who show ONLY ONCE, please help me.
thank you!
if i have understand your problem, must group your data,
select year,athlete_name,medal, count(*) "number of Medals"
from olym.olym_medals
join olym.olym_athlete_games on olym_athlete_games.id = olym_medals.athlete_game_id
join olym.olym_nations on olym_nations.id = olym_athlete_games.nation_id
join olym.olym_games on olym_games.id = Olym_athlete_games.game_id
join olym.olym_athletes on olym_athletes.id = olym_athlete_games.athlete_id
group by year,athlete_name,medal;
If I followed you correctly, you can use window functions:
select *
from (
select og.year, oa.athlete_name, om.medal, count(*) over(partition by oa.id) cnt
from olym.olym_medals om
join olym.olym_athlete_games oag on oag.id = om.athlete_game_id
join olym.olym_nations ona on ona.id = oag.nation_id
join olym.olym_games og on og.id = oag.game_id
join olym.olym_athletes oa on oa.id = oag.athlete_id
) t
where cnt > 1
order by athlete_name
Notes:
I am unsure why you were using distinct in the first place, so I removed it (I suspect it is actually not needed)
I added table aliases to shorten the query, and prefixed the columns in the select clause with the table they belong to (you might want to review that) - these are best practices when dealing with multi-table queries
Use GROUP BY and HAVING COUNT(*) > 1:
SELECT year,
athlete_name,
medal
FROM olym.olym_medals
INNER JOIN olym.olym_athlete_games
ON olym_athlete_games.id = olym_medals.athlete_game_id
INNER JOIN olym.olym_nations
ON olym_nations.id = olym_athlete_games.nation_id
INNER JOIN olym.olym_games
ON olym_games.id = Olym_athlete_games.game_id
INNER JOIN olym.olym_athletes
ON olym_athletes.id = olym_athlete_games.athlete_id
GROUP BY
year,
athlete_name,
medal
HAVING COUNT(*) > 1
ORDER BY athlete_name

SQL Selecting rows with not the same condition for all

I have to create SQL query that select persons datas. Every person has several grades and I have to select first by time for everyone. I don't know how do it because conditional is different for every person. Below is my current code which doesn't works.
SELECT s.sol_last_name,
g.grade_name,
MIN(sg.sol_grade_date_from)
FROM [dbo].[dim_s####] AS s
LEFT JOIN [dbo].[fact_s####_grade] AS sg ON s.sol_key = sg.sol_grade_sollers_key
LEFT JOIN [dbo].[dim_grade] AS g ON g.grade_key = sg.sol_grade_grade_key
GROUP BY s.sol_last_name,
g.grade_name
HAVING MIN(sg.sol_grade_date_from) = sg.sol_grade_date_from
You can put the earliest date in a subquery, and then inner join there:
SELECT s.sol_last_name,
g.grade_name,
sg.sol_grade_date_from
FROM [dbo].[dim_s####] AS s
INNER JOIN (
select sol_grade_grade_key
,min(sol_grade_date_from) as sol_grade_date_
from from [dbo].[dim_grade]
GROUP BY sol_grade_grade_key) AS g
ON g.grade_key = sg.sol_grade_grade_key
LEFT JOIN [dbo].[fact_s####_grade] AS sg
ON s.sol_key = sg.sol_grade_sollers_key
Use a Common Table Expression (cte) to save some typing. Then do a NOT EXISTS to return a row only if same sol_last_name has no older grade.
WITH CTE (sol_last_name, grade_name, grade_date_from) AS
(
SELECT s.sol_last_name,
g.grade_name,
sg.sol_grade_date_from
FROM [dbo].[dim_s####] AS s
LEFT JOIN [dbo].[fact_s####_grade] AS sg ON s.sol_key = sg.sol_grade_sollers_key
LEFT JOIN [dbo].[dim_grade] AS g ON g.grade_key = sg.sol_grade_grade_key
)
select sol_last_name, grade_name, grade_date_from
from cte as t1
where not exists (select 1 from cte t2
where t2.sol_last_name = t1.sol_last_name
and t2.grade_date_from < t2.grade_date_from)

SQL Join only if all records have a match

I have 3 tables:
CP_carthead (idOrder)
CP_cartrows (idOrder, idCartRow)
CP_shipping (idCartRow, idShipping, dateShipped)
There can be multiple idCartRows per idOrder.
I want to get all orders where all its idCartRows exist in CP_shipping. This seems like it should be simple, but I haven't found much on the web.
Here's my query now:
SELECT
s.idOrder
, s.LatestDateShipped
FROM
CP_carthead o
LEFT OUTER JOIN (
SELECT
MAX(s.dateShipped) [LatestDateShipped]
, r.idOrder
FROM
CP_shipping s
LEFT OUTER JOIN CP_cartrows r ON s.idCartRow = r.idCartRow
GROUP BY
r.idOrder
) s ON o.idOrder = s.idOrder
Your query is returning rows from "s" and not the orders. Based on your question, I came up with this query:
select o.*
from CP_Carthead o
where o.orderId in (select cr.idOrder
from cp_cartrows cr left outer join
cp_shipping s
on cr.idCartRow = s.IdCartrow
group by cr.idOrder
having count(s.idCartRow) = COUNT(*)
)
The subquery in the in statement is getting orders all of whose cartrows are in shipping.

Subquery with multiple joins involved

Still trying to get used to writing queries and I've ran into a problem.
Select count(region)
where (regionTable.A=1) in
(
select jxn.id, count(jxn.id) as counts, regionTable.A
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
group by jxn.id, regionTable.A
)
The inner query gives an ID number in one column, the amount of times they appear in the table, and then a bit attribute if they are in region A. The outer query works but the error I get is incorrect syntax near the keyword IN. Of the inner query, I would like a number of how many of them are in region A
You must specify table name in query before where
Select count(region)
from table
where (regionTable.A=1) in
And you must choose one of them.
where regionTable.A = 1
or
where regionTable.A in (..)
Your query has several syntax errors. Based on your comments, I think there is no need for a subquery and you want this:
select jxn.id, count(jxn.id) as counts, regionTable.A
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
where regionTable.A = 1
group by jxn.id, regionTable.A
which can be further simplified to:
select jxn.id, count(jxn.id) as counts
, 1 as A --- you can even omit this line
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
where regionTable.A = 1
group by jxn.id
You are getting the error because of this line:
where (regionTable.A=1)
You cannot specify a condition in a where in clause, it should only be column name
Something like this may be what you want:
SELECT COUNT(*)
FROM
(
select jxn.id, count(jxn.id) as counts, regionTable.A
from
jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
group by jxn.id, regionTable.A
) sq
WHERE sq.a = 1