Where am I going wrong with this SQL query? - sql

I am attempting to do the following:
Check to see if the table does not exist and if so, create the TABLE 'tmpTriangleTransfer'.
Check to see if the table exists and if so, DROP the TABLE 'tmpTriangleTransfer'.
Insert the data being pulled from the other tables into the 2nd -
5th columns of the TABLE 'tmpTriangleTransfer'.
Loop and for each row that exists in the TABLE 'tmpTriangleTransfer' update the 1st column with the declared information.
Return all of the information from that table (to be formatted into a report).
Can someone please help me figure out what I am doing wrong? I'm getting no results even though I know for a fact there are records (when I run just the SELECT statement on the last line, it shows records and when I run the SELECT DISTINCT statement in the middle, it shows the same records).
IF OBJECT_ID('tmpTriangleTransfer') IS NOT NULL
DROP TABLE tmpTriangleTransfer;
IF OBJECT_ID('tmpTriangleTransfer') IS NULL
CREATE TABLE tmpTriangleTransfer
(
CompanyName varchar(max),
OrderID decimal(19,2) NULL,
DriverID int NULL,
VehicleID int NULL,
Phone varchar(50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
BOL varchar(50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
);
INSERT INTO tmpTriangleTransfer (OrderID, BOL, DriverID, VehicleID, Phone)
SELECT DISTINCT tblOrder.OrderID AS OrderID, tblOrder.BOL AS BOL, tblOrderDrivers.DriverID AS DriverID, tblDrivers.VehicleID AS VehicleID, tblWorker.Phone AS Phone
FROM tblOrder WITH (NOLOCK)
INNER JOIN tblActiveOrders
ON tblOrder.OrderID = tblActiveOrders.OrderID
INNER JOIN tblOrderDrivers
ON tblOrder.OrderID = tblOrderDrivers.OrderID
INNER JOIN tblDrivers
ON tblOrderDrivers.DriverID = tblDrivers.DriverID
INNER JOIN tblWorker
ON tblDrivers.WorkerID = tblWorker.WorkerID
WHERE tblOrder.CustID = 7317
ORDER BY tblOrder.OrderID`
DECLARE #MaxRownum INT
SET #MaxRownum = (SELECT MAX(OrderID) FROM tmpTriangleTransfer)
DECLARE #Iter INT
SET #Iter = (SELECT MIN(OrderID) FROM tmpTriangleTransfer)
WHILE #Iter <= #MaxRownum
BEGIN
UPDATE tmpTriangleTransfer
SET tmpTriangleTransfer.CompanyName = 'Triangle'
WHERE tmpTriangleTransfer.CompanyName IS NULL;
SET #Iter = #Iter + 1
END
SELECT * from tmpTriangleTransfer WITH (NOLOCK)

Your existing query is far too complicated. In fact, you don't need a temporary table, the WHILE loop, or anything - just a single SELECT is all you need:
SELECT
'Triangle' AS CompanyName,
tblOrder.OrderId,
tblOrder.BOL,
tblOrderOrders.DriverID,
tblDrivers.VehicleID,
tblWorker.Phone
FROM
tblOrder
OUTER JOIN tblActiveOrders ON tblOrder.OrderID = tblActiveOrders.OrderID
OUTER JOIN tblOrderDrivers ON tblOrder.OrderID = tblOrderDrivers.OrderID
OUTER JOIN tblDrivers ON tblOrderDrivers.DriverID = tblDrivers.DriverID
OUTER JOIN tblWorker ON tblDrivers.WorkerID = tblWorker.WorkerID
WHERE
tblOrder.CustID = 7317
ORDER BY
tblOrder.OrderID
I've changed your query to use OUTER JOIN instead of INNER JOIN because I suspect this is the main reason for no data being returned. INNER JOIN requires rows to exist in both tables (relations) and I suspect that you have Orders without Drivers or that not every Order is in ActiveOrders. Change the joins to INNER JOIN if you know that related rows will always be present.
You can return literals in queries directly, like I'm doing in the SELECT 'Triangle' AS CompanyName part, whereas you were seemingly manually adding it to the output temporary-table.
Your code didn't seem to be doing anything that would require the WITH (NOLOCK) modifier - the fact it was repeated everywhere makes it look like a case of Cargo-Cult Programming.
Tip: In SQL, a SELECT statement, as written, is not representative of its logical execution order. It should instead be read in this order: FROM > WHERE > [GROUP BY >] SELECT > ORDER BY.
This is why in .NET Linq the .Select() call is often at the end, not the beginning, because previous Linq expressions define the data sources.
This query can be parameterised by converting it to a Table-defined Function that accepts CustID as a parameter, I also assume you have the company name "Triangle" stored in a table somewhere - embedding it as a literal value for a single query is a code-smell - what's so special about 7317 / "Triangle"?
Related note: Generally speaking, queries that only SELECT data (and don't perform any INSERT/UPDATE/DELETE/ALTER/CREATE statements) should be Table-valued UDFs or Views and not Stored Procedures - so that they can benefit from function-composition, query-composition and runtime execution plan optimizations that you cannot get with Stored Procedures.
If you're able to, see if you can remove the tbl prefix from the table names (Using "tbl" as a prefix has its defenders, but my own personal opinion is that it's an obsolete developer aid as today's database tooling shows type information, and it makes database refactoring harder (e.g. converting a table to a view).

Taken from a combination of the suggestion from Dai and the requirements of my employer:
`SELECT 'Triangle' AS CompanyName, tblOrder.OrderId AS OrderID, tblOrder.BOL AS BOL, tblOrderDrivers.DriverID AS DriverID, tblDrivers.VehicleID AS VehicleID, tblWorker.Phone AS Phone
FROM tblOrder WITH (NOLOCK)
INNER JOIN tblActiveOrders WITH (NOLOCK)
ON tblOrder.OrderID = tblActiveOrders.OrderID
INNER JOIN tblOrderDrivers WITH (NOLOCK)
ON tblOrder.OrderID = tblOrderDrivers.OrderID
INNER JOIN tblDrivers WITH (NOLOCK)
ON tblOrderDrivers.DriverID = tblDrivers.DriverID
INNER JOIN tblWorker WITH (NOLOCK)
ON tblDrivers.WorkerID = tblWorker.WorkerID
WHERE
tblOrder.CustID = 7317
ORDER BY
tblOrder.OrderID desc`

Related

Duplicate rows in SQL Server

I am having duplicate rows with the same storeactivityid show up in my results...
This is the primary key, so this should not happen. Is there something wrong with my joins that could cause this? I could use distinct, but that will not solve the issue here.
Any tips or advice? There are 3 duplicates showing for each result!
select pd.storeactivityid,e.EMPLOYEENAME,c.ChainName,c.UserCode as ChainNumber,
s.storenumber,s.StoreNameAndNumber,
pd.startdatetime,
pd.enddatetime,
cast((datediff(s, pd.startdatetime, pd.enddatetime) / 3600.0) as decimal(9,2)) as duration,
exceptioncodes,pe.Description,isnull(pd.approved, 0) as approved,
isnull(pd.comment, '') as comment,
pd.modifieddate
from payrolldetail pd with (nolock)
inner join payperiods pp with (nolock) on pd.enddatetime between pp.begindate and pp.enddate and pp.CompanyID = #companyid
left join stores s with (nolock) on pd.storeid = s.storeid
left join chains c with (nolock) on c.chainid = s.chaincode
left join employees e with (nolock) on pd.employeeid = e.employeeid
inner join payrollexceptions pe with (nolock) on pd.ExceptionCodes = pe.Code
where pd.companyid = #companyid
and cast(getdate() as date) between pp.begindate and pp.enddate
and exceptioncodes = #exceptioncodes
and pd.companyid = #companyid
If it is a primary key, you can be certain that in the actual table you do not have duplicate rows with the same storeactivityid.
Your query returns rows with the same storeactivityid because at least one of the joined tables has the matches the condition specified in the join.
My best guess it is due to the followoing join:
inner join payperiods pp with (nolock) on pd.enddatetime between pp.begindate and pp.enddate and pp.CompanyID = #companyid
Is it possible that a company has multiple payrolldetails within the same range of dates specified in the payperiods table?
i do not know what is in each of the tables,
but the easiest way i found to debug something like his is
select [storeactivityid],count([storeactivityid]) as [count]
from [<table>]
<Start adding joins in one at a time>
where [count] > 1
group by [storeactivityid]
If you use NOLOCK hint to be able to do 'dirty reads' (read uncommitted) and some modifications are happen in the same time on this tables, this may cause missing or double count of even unique rows!
If there is no activity on the server, no updates/inserts/deletes, than there is something inside data of your tables that caused duplicating, as other guys have already said.

Query with columns from 4 tables in SQL

Can anyone who knows SQL, specifically the flavor used in Microsoft Access 2013, tell me what I'm doing wrong here?
SELECT custid, custname, ordno, itemno, itemname
FROM cust
INNER JOIN order
ON cust.custid = order.custid
INNER JOIN orderitems
ON order.ordno = orderitems.ordno
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
I've already read other, similar questions, and tried the methods they used in their solutions, but I'm getting a "Syntax error in FROM clause.", almost no matter what I try.
* * *
SOLUTION: Thanks for the replies! In addition to adding square brackets around "order" and using TableName.ColumnName syntax in SELECT, I had to use parentheses for my multiple INNER JOINs. Here is the fixed code:
SELECT cust.custid, cust.custname, [order].ordno, orderitems.itemno, inv.itemname
FROM ((cust
INNER JOIN [order]
ON cust.custid = [order].custid)
INNER JOIN orderitems
ON [order].ordno = orderitems.ordno)
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
SELECT cust.custid --<-- Use two part name here
,cust.custname
,[order].ordno
,orderitems.itemno --<-- Only guessing here use the correct table name
,inv.itemname --<-- Only guessing here use the correct table name
FROM cust
INNER JOIN [order]
ON cust.custid = [order].custid --<-- used square brackets [] around ORDER as it is
INNER JOIN orderitems -- a key word.
ON [order].ordno = orderitems.ordno
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
In your Select Statament you need to use Two Part name i.e TableName.ColumnName since these column can exist in more than one Tables in your FROM clause you need to tell sql server that columns in your select coming from which table in your from clause.

constructing complex sql statement

I need to select and update a table looking up values from other tables. The idea is to select some details from order, but get the names of the foreign key values using the id
For the select I tried:
SELECT [Order].order_date,
[Order].total_price,
[Order].order_details,
vehicle.reg_no,
Staff.name,
stock.name
FROM
Order,
vehicle,
staff,
stock
WHERE
order.id = #order_id
AND vehicle.id = Order.vehicle_id
AND staff.id = Order.staff_id
AND stock.id = Order.product_id
for the update that i tried
UPDATE order
SET
total_price = #total_price,
order_detail = #order_detail,
product_id = (select is from stock where name = #product_name),
customer_id = (select id from customer where name = #customer_name),
vehicle_regno = (select reg_no from vehicle where name = #name)
WHERE (id = #id)
both does not return anything. i hope am clear enough to get some help, but if not pls i will provide more info.
thanks for helping
You might want to try converting you INNER JOINs to a LEFT JOIN for the SELECT statement.
SELECT [Order].order_date,
[Order].total_price,
[Order].order_details,
vehicle.reg_no,
Staff.name,
stock.name
FROM
[Order]
LEFT JOIN vehicle
ON vehicle.id = [Order].vehicle_id
LEFT JOIN staff
ON staff.id = [Order].staff_id
LEFT JOIN stock
ON stock.id = [Order].product_id
WHERE
[order].id = #order_id
The reasons why this would make a difference is if either a) you allow nulls in the fk fields or b) you don't have fk contraints which may point to a problem with your design.
Your update statement should update some rows provided that the value of #id exists in the ORDER table but as #Danny Varod already commented you won't get rows back only the number of rows affected
You are comparing stock.id with Order.product_id, could that be the problem?
Otherwise why it is not returning any rows we would need to know the content of the tables, perhaps you need a left join instead of inner join to some of them?

Ambiguous column name - is it though?

If I want to write a query with a simple join, I can do this:
select * from customer c
join order o
on c.customerid = o.customerid
where c.customerid = 100
and it all works fine. In this query, is there a reason why I have to specify a table alias - ie. c.customerid? Why can't I just write this:
select * from customer c
join order o
on c.customerid = o.customerid
where customerid = 100
I get the error Ambiguous column name 'customerid'. In this case, where there's only one column in the WHERE clause and it's the column on which I'm JOINing, is this actually "ambiguous"? Or is it just to comply with the ansi-standard (I'm guessing here - I don't know if it does comply) and to encourage good coding conventions?
For your specific example I can't think of any circumstances in which it would make a difference. However for an INNER JOIN on a string column it could do as below.
DECLARE #customer TABLE
(customerid CHAR(3) COLLATE Latin1_General_CI_AS)
INSERT INTO #customer VALUES('FOO');
DECLARE #order TABLE
(customerid CHAR(3) COLLATE Latin1_General_CS_AS)
INSERT INTO #order VALUES('FOO');
SELECT *
FROM #customer c
JOIN #order o
ON c.customerid = o.customerid COLLATE Latin1_General_CS_AS
WHERE c.customerid = 'Foo' /*Returns 1 row*/
SELECT *
FROM #customer c
JOIN #order o
ON c.customerid = o.customerid COLLATE Latin1_General_CS_AS
WHERE o.customerid = 'Foo' /*Returns 0 rows*/
Omitting the table alias really does make for an ambiguous column reference. Just make your join a left join, and you'll immediately see why:
select * from customer c
left join order o
on c.customerid = o.customerid
where customerid = 100 -- here, the semantics are quite different
Another reason: One column could be of type INTEGER, the other of type SMALLINT. Which one to use for the filter? (This might have implications on the execution plan). An even better example is given by Martin Smith
So in general, you wouldn't gain much by making SQL more "forgiving", while at the same time introducing new sources of error. What you could do with some databases (not SQL Server), however is this:
select * from customer c
join order o
using (customerid)
where customerid = 100
Or this (if customerid is the only common column name)
select * from customer c
natural join order o
where customerid = 100
You are getting the error because the customerid column exists in both the order and customer tables and SQL doesn't know which column the condition should be applied to.
After JOINing two tables, the resulting table contains 2 columns having the same name of customerid. So you need to tell the WHERE clause which column to use by adding the table name as prefix.
Well...you know that the result set will only contain entries with exactly the same customerid, the database server however does not, because he doesn't "understand" what you are specifying. And if you had a join that does not have both customerids exactly the same in the result set, you will be happy, that the server distinguishes them. ;)
Ambiguous column error only come when we need to do some operation on a field which has in more then one table so in this case SQL can not recognize that from which table filed it need to operate.

Converting a nested sql where-in pattern to joins

I have a query that is returning the correct data to me, but being a developer rather than a DBA I'm wondering if there is any reason to convert it to joins rather than nested selects and if so, what it would look like.
My code currently is
select * from adjustments where store_id in (
select id from stores where original_id = (
select original_id from stores where name ='abcd'))
Any references to the better use of joins would be appreciated too.
Besides any likely performance improvements, I find following much easier to read.
SELECT *
FROM adjustments a
INNER JOIN stores s ON s.id = a.store_id
INNER JOIN stores s2 ON s2.original_id = s.original_id
WHERE s.name = 'abcd'
Test script showing my original fault in ommitting original_id
DECLARE #Adjustments TABLE (store_id INTEGER)
DECLARE #Stores TABLE (id INTEGER, name VARCHAR(32), original_id INTEGER)
INSERT INTO #Adjustments VALUES (1), (2), (3)
INSERT INTO #Stores VALUES (1, 'abcd', 1), (2, '2', 1), (3, '3', 1)
/*
OP's Original statement returns store_id's 1, 2 & 3
due to original_id being all the same
*/
SELECT * FROM #Adjustments WHERE store_id IN (
SELECT id FROM #Stores WHERE original_id = (
SELECT original_id FROM #Stores WHERE name ='abcd'))
/*
Faulty first attempt with removing original_id from the equation
only returns store_id 1
*/
SELECT a.store_id
FROM #Adjustments a
INNER JOIN #Stores s ON s.id = a.store_id
WHERE s.name = 'abcd'
If you would use joins, it would look like this:
select *
from adjustments
inner join stores on stores.id = adjustments.store_id
inner join stores as stores2 on stores2.original_id = stores.original_id
where stores2.name = 'abcd'
(Apparently you can omit the second SELECT on the stores table (I left it out of my query) because if I'm interpreting your table structure correctly,
select id from stores where original_id = (select original_id from stores where name ='abcd')
is the same as
select * from stores where name ='abcd'.)
--> edited my query back to the original form, thanks to Lieven for pointing out my mistake in his answer!
I prefer using joins, but for simple queries like that, there is normally no performance difference. SQL Server treats both queries the same internally.
If you want to be sure, you can look at the execution plan.
If you run both queries together, SQL Server will also tell you which query took more resources than the other (in percent).
A slightly different approach:
select * from adjustments a where exists
(select null from stores s1, stores s2
where a.store_id = s1.id and s1.original_id = s2.original_id and s2.name ='abcd')
As say Microsoft here:
Many Transact-SQL statements that include subqueries can be
alternatively formulated as joins. Other questions can be posed only
with subqueries. In Transact-SQL, there is usually no performance
difference between a statement that includes a subquery and a
semantically equivalent version that does not. However, in some cases
where existence must be checked, a join yields better performance.
Otherwise, the nested query must be processed for each result of the
outer query to ensure elimination of duplicates. In such cases, a join
approach would yield better results.
Your case is exactly when Join and subquery gives the same performance.
Example when subquery can not be converted to "simple" JOIN:
select Country,TR_Country.Name as Country_Translated_Name,TR_Country.Language_Code
from Country
JOIN TR_Country ON Country.Country=Tr_Country.Country
where country =
(select top 1 country
from Northwind.dbo.Customers C
join
Northwind.dbo.Orders O
on C.CustomerId = O.CustomerID
group by country
order by count(*))
As you can see, every country can have different name translations so we can not just join and count records (in that case, countries with larger quantities of translations will have more record counts)
Of cource, you can can transform this example to:
JOIN with derived table
CTE
but it is an other tale-)