Ms access get row number from a subquery not table - sql

I have the following Ms Access query that retrieves data successfully:
SELECT stockInventory.purchaseId, stockInventory.itemId, item.itemName, stockInventory.unitId, unit.unitDesc, stockInventory.quantity, stockInventory.costPrice
FROM unit INNER JOIN (item INNER JOIN stockInventory ON item.itemId = stockInventory.itemId) ON unit.unitId = stockInventory.unitId
WHERE (((stockInventory.purchaseId)=1))
Now I want to retrieve these data with row number!
I tried the following:
SELECT A.*, ( SELECT COUNT(*) FROM A WHERE A.itemId>=itemId ) as rowNo
FROM
(
SELECT stockInventory.purchaseId, stockInventory.itemId, item.itemName, stockInventory.unitId, unit.unitDesc, stockInventory.quantity, stockInventory.costPrice
FROM unit INNER JOIN (item INNER JOIN stockInventory ON item.itemId = stockInventory.itemId) ON unit.unitId = stockInventory.unitId
WHERE (((stockInventory.purchaseId)=1))
) AS A;
But it says: The Microsoft access database engine cannot find the input table or query 'A' as the following picture:
How can I solve this problem?

The additional SELECT part
( SELECT COUNT(*) FROM A WHERE A.itemId>=itemId ) as rowNo
is a separate query that doesn't know about A.
I think you must save your original query (= the subquery) as new named query, then you can reference it in both SELECT parts.
SELECT A.*,
( SELECT COUNT(*) FROM mySubquery AS B WHERE B.itemId>=A.itemId ) as rowNo
FROM mySubquery AS A
Now it also gets clearer that you need two instances of the subquery (A and B).
I hope you don't have too many records, because performance will probably be bad. But that wasn't the focus here...

Consider directly adding rowNo subquery in original query:
SELECT (SELECT Count(*) FROM stockInventory AS sub
WHERE sub.itemId <= stockInventory.itemId) AS rowNo,
stockInventory.purchaseId, stockInventory.itemId, item.itemName,
stockInventory.unitId, unit.unitDesc, stockInventory.quantity,
stockInventory.costPrice
FROM unit
INNER JOIN (item
INNER JOIN stockInventory
ON item.itemId = stockInventory.itemId)
ON unit.unitId = stockInventory.unitId
WHERE (((stockInventory.purchaseId)=1))

Related

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

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

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

SELECT statement where rows are omitted based on another table

Table with orders has another table with positions. I want the orders table to show but then only have the most up to-date position on it. Below is a picture of the 3 rows I want showing. Omit the rest.
SELECT DispatchTable.ordernumber, DispatchTable.truck,
DispatchTable.driver, DispatchTable.actualpickup,
DispatchTable.actualdropoff, orders.pickupdateandtime,
orders.dropoffdateandtime, Truck002.lastposition,
Truck002.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN Truck002 ON DispatchTable.truck = Truck002.name
WHERE (orders.status = 'onRoute')
Assuming that you want the row having the latest lastdateandtime for the truck name, this should work:
SELECT DispatchTable.ordernumber,
DispatchTable.truck,
DispatchTable.driver,
DispatchTable.actualpickup,
DispatchTable.actualdropoff,
orders.pickupdateandtime,
orders.dropoffdateandtime,
TruckLatest.lastposition,
TruckLatest.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN (SELECT name,
lastposition,
lastdateandtime
FROM Truck002 Truck1
WHERE lastdateandtime =
(SELECT MAX(lastdateandtime)
FROM Truck002 Truck2
WHERE Truck2.name = Truck1.name)) TruckLatest
ON DispatchTable.truck = TruckLatest.name
WHERE (orders.status = 'onRoute')
If I understand correctly, you can get the most recent record for a truck using ROW_NUMBER():
SELECT dt.ordernumber, dt.truck,
dt.driver, dt.actualpickup,
dt.actualdropoff, o.pickupdateandtime,
o.dropoffdateandtime, t.lastposition,
t.lastdateandtime
FROM DispatchTable dt INNER JOIN
orders o
ON dt.ordernumber = o.id INNER JOIN
(SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.name ORDER BY t.lastdateandtime DESC) as seqnum
FROM Truck002 t
) t
ON dt.truck = t.name
WHERE o.status = 'onRoute' AND seqnum = 1;
Firstly, why are you using Truck002's name field rather than its id field as the link to DispacthTable? This is considered a less efficient way of doing it than using id (which is either a numerical field or a shorter string than name).
Secondly, you should mention in your Question that each Order can have many DispatchTable's and that each DispacthTable can have many Truck002's, otherwise many people will start by assuming that it is the other way round between DispatchTable and Truck002.
Thirdly, please try...
SELECT DispatchTable.ordernumber,
DispatchTable.truck,
DispatchTable.driver,
DispatchTable.actualpickup,
DispatchTable.actualdropoff,
orders.pickupdateandtime,
orders.dropoffdateandtime,
Truck002.lastposition,
Truck002.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN Truck002 ON DispatchTable.truck = Truck002.name
WHERE (orders.status = 'onRoute')
GROUP BY ordernumber
HAVING lastdateandtime = MAX( lastdateandtime )
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://msdn.microsoft.com/en-us/library/bb177906(v=office.12).aspx (on HAVING)
https://www.w3schools.com/sql/sql_having.asp (on HAVING)
https://msdn.microsoft.com/en-us/library/bb177905(v=office.12).aspx (on GROUP BY)
https://www.w3schools.com/sql/sql_groupby.asp (on GROUP BY)

SQL not efficient enough, tuning assistance required

We have some SQL that is ok on smaller data volumes but poor once we scale up to selecting from larger volumes. Is there a faster alternative style to achieve the same output as below? The idea is to pull back a single unique row to get latest version of the data... The SQL does reference another view but this view runs very fast - so we expect the issue is here below and want to try a different approach
SELECT *
FROM
(SELECT (select CustomerId from PremiseProviderVersionsToday
where PremiseProviderId = b.PremiseProviderId) as CustomerId,
c.D3001_MeterId, b.CoreSPID, a.EnteredBy,
ROW_NUMBER() OVER (PARTITION BY b.PremiseProviderId
ORDER BY a.effectiveDate DESC) AS rowNumber
FROM PremiseMeterProviderVersions a, PremiseProviders b,
PremiseMeterProviders c
WHERE (a.TransactionDateTimeEnd IS NULL
AND a.PremiseMeterProviderId = c.PremiseMeterProviderId
AND b.PremiseProviderId = c.PremiseProviderId)
) data
WHERE data.rowNumber = 1
As Bilal Ayub stated above, the correlated subquery can result in performance issues. See here for more detail. Below are my suggestions:
Change all to explicit joins (ANSI standard)
Use aliases that are more descriptive than single characters (this is mostly to help readers understand what each table does)
Convert data subquery to a temp table or cte (temp tables and ctes usually perform better than subqueries)
Note: normally, you should explicitly create and insert into your temp table but I chose not to do that here as I do not know the data types of your columns.
SELECT d.CustomerId
, c.D3001_MeterId
, b.CoreSPID
, a.EnteredBy
, rowNumber = ROW_NUMBER() OVER(PARTITION BY b.PremiseProviderId ORDER BY a.effectiveDate DESC)
INTO #tmp_RowNum
FROM PremiseMeterProviderVersions a
JOIN PremiseMeterProviders c ON c.PremiseMeterProviderId = a.PremiseMeterProviderId
JOIN PremiseProviders b ON b.PremiseProviderId = c.PremiseProviderId
JOIN PremiseProviderVersionsToday d ON d.PremiseProviderId = b.PremiseProviderId
WHERE a.TransactionDateTimeEnd IS NULL
SELECT *
FROM #tmp_RowNum
WHERE rowNumber = 1
You are running a correlated query that will run in loop, if size of table is small it will be faster, i would suggest to change it and try to join the table in order to get customerid.
(select CustomerId from PremiseProviderVersionsToday where PremiseProviderId = b.PremiseProviderId) as CustomerId
Consider derived tables including an aggregate query that calculates maximum EffectoveDate by PremiseProviderId and unit level query, each using explicit joins (current ANSI SQL standard) and not implicit as you currently use:
SELECT data.*
FROM
(SELECT t.CustomerId, c.D3001_MeterId, b.CoreSPID, a.EnteredBy,
b.PremiseProviderId, a.EffectiveDate
FROM PremiseMeterProviders c
INNER JOIN PremiseMeterProviderVersions a
ON a.PremiseMeterProviderId = c.PremiseMeterProviderId
AND a.TransactionDateTimeEnd IS NULL
INNER JOIN PremiseProviders b
ON b.PremiseProviderId = c.PremiseProviderId
INNER JOIN PremiseProviderVersionsToday t
ON t.PremiseProviderId = b.PremiseProviderId
) data
INNER JOIN
(SELECT b.PremiseProviderId, MAX(a.EffectiveDate) As MaxEffDate
FROM PremiseMeterProviders c
INNER JOIN PremiseMeterProviderVersions a
ON a.PremiseMeterProviderId = c.PremiseMeterProviderId
AND a.TransactionDateTimeEnd IS NULL
INNER JOIN PremiseProviders b
ON b.PremiseProviderId = c.PremiseProviderId
GROUP BY b.PremiseProviderId
) agg
ON data.PremiseProviderId = agg.PremiseProviderId
AND data.EffectiveDate = agg.MaxEffDate

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)