SQL Error of Undefined Column - sql

SELECT
Stockmain.VRNOA,
item.description as item_description,
party.name as party_name,
stockmain.vrdate,
stockdetail.qty,
stockdetail.rate,
stockdetail.amount,
ROW_NUMBER() OVER (ORDER BY VRDATE) AS RowNum
FROM StockMain
INNER JOIN StockDetail
ON StockMain.stid = StockDetail.stid
INNER JOIN party
ON party.party_id = stockmain.party_id
INNER JOIN item
ON item.item_id = stockdetail.item_id
WHERE stockmain.etype='purchase' AND RowNum BETWEEN 1 and (1 + 100)
ORDER BY VRDATE DESC
I am trying to execute this query on SQL Server and it keeps giving this error
Invalid column name 'RowNum'.
Can anyone please have a look and tell me what I'm doing wrong here?
Update
The query I was originally using was
SELECT
*
FROM (
SELECT
Stockmain.VRNOA,
item.description as item_description,
party.name as party_name,
stockmain.vrdate,
stockdetail.qty,
stockdetail.rate,
stockdetail.amount,
ROW_NUMBER() OVER (ORDER BY VRDATE DESC) AS RowNum --< ORDER BY
FROM StockMain
INNER JOIN StockDetail
ON StockMain.stid = StockDetail.stid
INNER JOIN party
ON party.party_id = stockmain.party_id
INNER JOIN item
ON item.item_id = stockdetail.item_id
WHERE stockmain.etype='purchase'
) AS MyDerivedTable
WHERE
MyDerivedTable.RowNum BETWEEN 1 and 5
but the data in the data is immense and using this query slows down the retrieval process, so I modified the query as above and now I'm getting this error of undefined column.

It looks like you're trying to perform a paging operation, or at least a query that is greatly simplified using the new paging operators in SQL Server 2012, OFFSET and FETCH:
SELECT
Stockmain.VRNOA,
item.description as item_description,
party.name as party_name,
stockmain.vrdate,
stockdetail.qty,
stockdetail.rate,
stockdetail.amount
FROM StockMain
INNER JOIN StockDetail
ON StockMain.stid = StockDetail.stid
INNER JOIN party
ON party.party_id = stockmain.party_id
INNER JOIN item
ON item.item_id = stockdetail.item_id
WHERE stockmain.etype='purchase'
ORDER BY VRDATE DESC
OFFSET 0 ROWS
FETCH NEXT 100 ROWS ONLY
For more information, please see the following: http://www.dbadiaries.com/new-t-sql-features-in-sql-server-2012-offset-and-fetch

Put it in a subquery, the ROW_NUMBER() can't be used in the WHERE clause, not to mention you can't use aliases created in the SELECT list in the WHERE clause:
SELECT *
FROM (SELECT
Stockmain.VRNOA,
item.description as item_description,
party.name as party_name,
stockmain.vrdate,
stockdetail.qty,
stockdetail.rate,
stockdetail.amount,
ROW_NUMBER() OVER (ORDER BY VRDATE) AS RowNum
FROM StockMain
INNER JOIN StockDetail
ON StockMain.stid = StockDetail.stid
INNER JOIN party
ON party.party_id = stockmain.party_id
INNER JOIN item
ON item.item_id = stockdetail.item_id
WHERE etype='purchase'
)sub
WHERE RowNum BETWEEN 1 and (1 + 100)
ORDER BY VRDATE DESC
Update: After seeing your update it's clear you've got a working query but are trying to optimize it, even if you could move the ROW_NUMBER() inside the main query it wouldn't improve performance, it still has to perform the intensive ORDER on the full data set. Indexing VRDATE will help.

This is a situation where you need to use a memory table or a temp table;
to use a memory table;
DECLARE #Table TABLE(ROWID int identity,<all your columns>)
INSERT INTO #Table (<your columns>)
SELECT
Stockmain.VRNOA,
item.description as item_description,
party.name as party_name,
stockmain.vrdate,
stockdetail.qty,
stockdetail.rate,
stockdetail.amount,
ROW_NUMBER() OVER (ORDER BY VRDATE) AS RowNum
FROM StockMain
INNER JOIN StockDetail
ON StockMain.stid = StockDetail.stid
INNER JOIN party
ON party.party_id = stockmain.party_id
INNER JOIN item
ON item.item_id = stockdetail.item_id
WHERE etype='purchase'
SELECT <columns you need> FROM #Table
If you use a #temp table, make sure to drop it after the SELECT statement.

Related

inner join with two selects sql

I am trying to implement an inner join to compare values of two tables, however failing for some reason and the query is returning zero columns.
I have two tables security and security_his and trying to join them on columns SECURITY_ID and INVESTMENT_OBJECTIVE. Query is as follows
SELECT *
FROM SECURITY origin
INNER JOIN (
SELECT *
FROM SECURITY_HIS t2
WHERE DATED = (
SELECT MAX(DATED)
FROM SECURITY_HIS t1
WHERE t1.SECURITY_ID = t2.SECURITY_ID
)
) history ON origin.SECURITY_ID = history.SECURITY_ID
AND origin.INVESTMENT_OBJECTIVE = history.INVESTMENT_OBJECTIVE;
WITH cte as (
SELECT S.*,
row_number() over
(partition by S.SECURITY_ID ORDER BY SH.DATED DESC)
FROM SECURITY S
JOIN SECURITY_HIS SH
ON S.SECURITY_ID = SH.SECURITY_ID
AND S.INVESTMENT_OBJECTIVE = SH.INVESTMENT_OBJECTIVE
)
SELECT *
FROM cte
WHERE rn = 1
You have no GROUP BY on the innermost query, so only a single value, maxed over the entire table, is returned. However your query can also be simplified for easier understanding:
SELECT origin.*, history.Dated
FROM SECURITY origin
INNER JOIN (
SELECT
SECURITY_ID,
INVESTMENT_OBJECTIVE,
MaxDated = MAX(DATED)
FROM SECURITY_HIS t2
GROUP BY
SECURITY_ID,
INVESTMENT_OBJECTIVE
) history ON origin.SECURITY_ID = history.SECURITY_ID
AND origin.INVESTMENT_OBJECTIVE = history.INVESTMENT_OBJECTIVE

Join two tables returning only one row from the second table

I have this query:
SELECT t_ticket.ticketID, t_ticket.addedDate, t_ticket.question,
t_ticket.code, t_ticket.priority, t_actionTicket.addedDateAction, t_actionTicket.title
FROM t_actionTicket INNER JOIN
t_ticket ON t_actionTicket.ticketID_FK = t_ticket.ticketID INNER JOIN
(SELECT ticketID_FK, MAX(addedDateAction) AS maxDate
FROM t_actionTicket AS t_actionTicket_1
WHERE (t_actionTicket.userID_FK <> #userid)
GROUP BY ticketID_FK) AS b ON t_actionTicket.ticketID_FK = b.ticketID_FK AND t_actionTicket.addedDateAction = b.maxDate
WHERE (t_ticket.supporterID_FK IN
(SELECT supporterID
FROM t_Supporter
WHERE (userID_FK = #userid)))
I want to return just the latest record in t_actionTicket table for each row in t_ticket table that t_actionTicket.userID_FK <> #userid.
but I have this error:
The multi-part identifier "t_actionTicket.userID_FK" could not be
bound.
Problem in your query is
FROM t_actionTicket AS t_actionTicket_1
WHERE t_actionTicket.userID_FK <> #userid -- here
You cannot use t_actionTicket alias name inside inner join select query. You need to use t_actionTicket_1. It is possible only in sub-query
Try this better way of doing it
;WITH cte
AS (SELECT t_ticket.ticketID,
t_ticket.addedDate,
t_ticket.question,
t_ticket.code,
t_ticket.priority,
t_actionTicket.addedDateAction,
t_actionTicket.title,
Row_number()
OVER(
partition BY ticketID_FK
ORDER BY addedDateAction DESC) RN
FROM t_actionTicket
INNER JOIN t_ticket
ON t_actionTicket.ticketID_FK = t_ticket.ticketID
WHERE t_ticket.supporterID_FK IN (SELECT supporterID
FROM t_Supporter
WHERE userID_FK = #userid))
SELECT *
FROM cte
WHERE rn = 1
You can write this logic using row_number() instead of additional nested queries:
SELECT t.ticketID, t.addedDate, t.question, t.code, t.priority,
ta.addedDateAction, ta.title AS Expr1
FROM t_Ticket t INNER JOIN
(SELECT ta.*,
ROW_NUMBER() OVER (PARTITION BY ta.ticketID_FK ORDER BY ta.addedDateAction DESC) as seqnum
FROM t_actionTicket ta
) ta
ON t.ticketId = ta.ticketId_FK and ta.seqnum = 1
WHERE t.supporterID_FK IN (SELECT supporterID
FROM t_Supporter
WHERE userID_FK = #userid
);
Note that table aliases make the query easier to write and to read.
Try this query
SELECT t_ticket.ticketID, t_ticket.addedDate, t_ticket.question,
t_ticket.code, t_ticket.priority, t_actionTicket.addedDateAction, t_actionTicket.title
FROM t_actionTicket
INNER JOIN t_ticket ON t_actionTicket.ticketID_FK = t_ticket.ticketID
INNER JOIN (SELECT ticketID_FK, MAX(addedDateAction) AS maxDate
FROM t_actionTicket AS t_actionTicket_1
WHERE (t_actionTicket_1.userID_FK <> #userid)
GROUP BY ticketID_FK) AS b ON t_actionTicket.ticketID_FK = b.ticketID_FK AND t_actionTicket.addedDateAction = b.maxDate
WHERE (t_ticket.supporterID_FK IN
(SELECT supporterID
FROM t_Supporter
WHERE (userID_FK = #userid)))

SQL Query to return last value from a number of tags

I hope you might be able to help. I'm a novice at SQL so this one is starting to bug me.
Currently I am collecting data every day for a Meter Name. This data is currently being logged in a table with the columns TimeStamp, Name, Value. However I would like to create a query which will only return the most recent (last) value recorded against each Name in the table.
I've built this query so far but the Top 1 syntax doesn't seem to be what I need.
SELECT Top 1 (DataLog.Timestamp), MeterTags.Name, DataLog.Value
FROM Meters
INNER JOIN MeterTags
ON Meters.MeterId = MeterTags.MeterId
INNER JOIN DataLog
ON MeterTags.MeterTagId = DataLog.MeterTagId
WHERE Meters.MeterTypeId = 8
GROUP By MeterTags.Name, DataLog.Timestamp
Any advice you could give would be appreciated.
Thanks in advance.
You can use ROW_NUMBER to give each record a rownumber (resetting to 0 for each MeterTags.Name) then just select the first for each name:
WITH CTE AS
( SELECT DataLog.Timestamp,
MeterTags.Name,
DataLog.Value,
RowNumber = ROW_NUMBER() OVER(PARTITION BY MeterTags.Name
ORDER BY DataLog.TimeStamp DESC)
FROM Meters
INNER JOIN MeterTags
ON Meters.MeterId = MeterTags.MeterId
INNER JOIN DataLog
ON MeterTags.MeterTagId = DataLog.MeterTagId
WHERE Meters.MeterTypeId = 8
)
SELECT CTE.Timestamp,
CTE.Name,
CTE.Value
FROM CTE
WHERE CTE.RowNumber = 1;
Another solution is to use the TOP 1 inside an APPLY:
SELECT DataLog.Timestamp,
MeterTags.Name,
DataLog.Value
FROM Meters
INNER JOIN MeterTags
ON Meters.MeterId = MeterTags.MeterId
CROSS APPLY
( SELECT TOP 1 TimeStamp, Value
FROM DataLog
WHERE MeterTags.MeterTagId = DataLog.MeterTagId
ORDER BY TimeStamp DESC
) DataLog
WHERE Meters.MeterTypeId = 8;
Try below query
select Timestamp,Name,Value
from
(
SELECT (DataLog.Timestamp), MeterTags.Name, DataLog.Value,rownum,ROW_NUMBER() OVER
(PARTITION BY MeterTags.Name ORDER BY DataLog.Timestamp desc) AS rownum FROM Meters
INNER JOIN MeterTags
ON Meters.MeterId = MeterTags.MeterId
INNER JOIN DataLog
ON MeterTags.MeterTagId = DataLog.MeterTagId
)data
where rownum=1

counting items that match within select statement

We have a stored procedure that executes against some fairly large tables and while joining to a larger table it is also keeping a tally of how many records match the corresponding batch_id. What I am trying to figure out is can I improve this with a function for the count or some other means? Trying to get rid of the nested SELECT COUNT(*) statement. The CCTransactions table is 1.4 million rows and the BatchItems is 6.6 million rows.
SELECT a.ItemAuthID, a.FeeAuthID, a.Batch_ID, a.ItemAuthCode,
a.FeeAuthCode, b.Amount, b.Fee,
(SELECT COUNT(*) FROM BatchItems WHERE Batch_ID = a.Batch_ID) AS BatchCount,
ItemBillDate, FeeBillDate, b.AccountNumber,
b.Itemcode, ItemAuthToken, FeeAuthToken,
cc.ItemMerchant, cc.FeeMerchant
FROM CCTransactions a WITH(NOLOCK)
INNER JOIN BatchItems b WITH(NOLOCK)
ON a.Batch_ID = b.Batch_ID
INNER JOIN CCConfig cc WITH(NOLOCK)
ON a.ClientCode = cc.ClientCode
WHERE ((ItemAuthCode > '' AND ItemBillDate IS NULL)
OR (FeeAuthCode > '' AND FeeBillDate IS NULL))
AND TransactionDate BETWEEN DATEADD(d,-7,GETDATE())
AND convert(char(20),getdate(),101) + ' ' + #Cutoff
ORDER BY TransactionDate
When your DBMS supports WIndowed Aggregate Functions you can rewrite it to
COUNT(*) OVER (PARTITION BY Batch_ID)
Of course this only returns the number of rows per Batch_ID returned by the SELECT. if the inner join results in less rows, it's not the correct number.
Then it might be more efficient (depending on your DBMS) to rewrite the Scalar Subquery to a join:
SELECT a.ItemAuthID, a.FeeAuthID, a.Batch_ID, a.ItemAuthCode,
a.FeeAuthCode, b.Amount, b.Fee,
dt.BatchCount,
ItemBillDate, FeeBillDate, b.AccountNumber,
b.Itemcode, ItemAuthToken, FeeAuthToken,
cc.ItemMerchant, cc.FeeMerchant
FROM CCTransactions a WITH(NOLOCK)
INNER JOIN BatchItems b WITH(NOLOCK)
ON a.Batch_ID = b.Batch_ID
INNER JOIN CCConfig cc WITH(NOLOCK)
ON a.ClientCode = cc.ClientCode
INNER JOIN
(
SELECT BatchCount, COUNT(*) AS BatchCount
FROM BatchItems
GROUP BY Batch_ID
) AS dt ON a.Batch_ID = dt.Batch_ID
WHERE ((ItemAuthCode > '' AND ItemBillDate IS NULL)
OR (FeeAuthCode > '' AND FeeBillDate IS NULL))
AND TransactionDate BETWEEN DATEADD(d,-7,GETDATE())
AND convert(CHAR(20),getdate(),101) + ' ' + #Cutoff
ORDER BY TransactionDate

Complex Full Outer Join

Sigh ... can anyone help? In the SQL query below, the results I get are incorrect. There are three (3) labor records in [LaborDetail]
Hours / Cost
2.75 / 50.88
2.00 / 74.00
1.25 / 34.69
There are two (2) material records in [WorkOrderInventory]
Material Cost
42.75
35.94
The issue is that the query incorrectly returns the following:
sFunction cntWO sumLaborHours sumLaborCost sumMaterialCost
ROBOT HARNESS 1 12 319.14 236.07
What am I doing wrong in the query that is causing the sums to be multiplied? The correct values are sumLaborHours = 6, sumLaborCost = 159.57, and sumMaterialCost = 78.69. Thank you for your help.
SELECT CASE WHEN COALESCE(work_orders.location, Work_Orders_Archived.location) IS NULL
THEN '' ELSE COALESCE(work_orders.location, Work_Orders_Archived.location) END AS sFunction,
(SELECT COUNT(*)
FROM work_orders
FULL OUTER JOIN Work_Orders_Archived
ON work_orders.order_number = Work_Orders_Archived.order_number
WHERE COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = '919630') AS cntWO,
SUM(Laborhours) AS sumLaborHours,
SUM(LaborCost) AS sumLaborCost,
SUM(MaterialCost*MaterialQuanity) AS sumMaterialCost
FROM work_orders
FULL OUTER JOIN Work_Orders_Archived
ON work_orders.order_number = Work_Orders_Archived.order_number
LEFT OUTER JOIN
(SELECT HoursWorked AS Laborhours, TotalDollars AS LaborCost, WorkOrderNo
FROM LaborDetail) AS LD
ON COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = LD.WorkOrderNo
LEFT OUTER JOIN
(SELECT UnitCost AS MaterialCost, Qty AS MaterialQuanity, OrderNumber
FROM WorkOrderInventory) AS WOI
ON COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = WOI.OrderNumber
WHERE COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = '919630'
GROUP BY CASE WHEN COALESCE(work_orders.location, Work_Orders_Archived.location) IS NULL
THEN '' ELSE COALESCE(work_orders.location, Work_Orders_Archived.location) END
ORDER BY sFunction
Try using the SUM function inside a derived table subquery when doing the full join to "WorkOrderInventory" like so...
select
...
sum(hrs) as sumlaborhrs,
sum(cost) as sumlaborcost,
-- calculate material cost in subquery
summaterialcost
from labordetail a
full outer join
(select ordernumber, sum(materialcost) as summaterialcost
from WorkOrderInventory
group by ordernumber
) b on a.workorderno = b.ordernumber
i created a simple sql fiddle to demonstrate this (i simplified your query for examples sake)
Looks to me that work_orders and work_orders_archived contains the same thing and you need both tables as if they were one table. So you could instead of joining create a UNION and use it as if it was one table:
select location as sfunction
from
(select location
from work_orders
union location
from work_orders_archived)
Then you use it to join the rest. What DBMS are you on? You could use WITH. But this does not exist on MYSQL.
with wo as
(select location as sfunction, order_number
from work_orders
union location, order_number
from work_orders_archived)
select sfunction,
count(*)
SUM(Laborhours) AS sumLaborHours,
SUM(LaborCost) AS sumLaborCost,
SUM(MaterialCost*MaterialQuanity) AS sumMaterialCost
from wo
LEFT OUTER JOIN
(SELECT HoursWorked AS Laborhours, TotalDollars AS LaborCost, WorkOrderNo
FROM LaborDetail) AS LD
ON COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = LD.WorkOrderNo
LEFT OUTER JOIN
(SELECT UnitCost AS MaterialCost, Qty AS MaterialQuanity, OrderNumber
FROM WorkOrderInventory) AS WOI
ON COALESCE(work_orders.order_number, Work_Orders_Archived.order_number) = WOI.OrderNumber
where wo.order_number = '919630'
group by sfunction
order by sfunction
The best guess is that the work orders appear more than once in one of the tables. Try these queries to check for duplicates in the two most obvious candidate tables:
select cnt, COUNT(*), MIN(order_number), MAX(order_number)
from (select order_number, COUNT(*) as cnt
from work_orders
group by order_number
) t
group by cnt
order by 1;
select cnt, COUNT(*), MIN(order_number), MAX(order_number)
from (select order_number, COUNT(*) as cnt
from work_orders_archived
group by order_number
) t
group by cnt
order by 1;
If either returns a row where cnt is not 1, then you have duplicates in the tables.