SUB SELECT QUERY WITH INNER JOIN giving Unexpected results - sql

This is my query inside my stored procedure
DECLARE #branch uniqueidentifier
SELECT #branch = Branch_Id
FROM Student_Master
WHERE Student_Id = #studentid
SELECT
CompanyMaster.unique_id_company,
Job_College.College_Id,
Job_Branch.Branch_Id,
CompanyMaster.name,
Job_Master.Job_Post_Date, Job_Master.Job_Title,
Job_Master.Salary, Job_Master.Job_Location,
Department_Master.Department_Name
FROM
Job_Master
INNER JOIN
Company_Master ON Job_Master.Company_ID = CompanyMaster.unique_id_company
INNER JOIN
Department_Master ON Department_Master.Department_Id = Job_Master.Department_Id
INNER JOIN
Job_College ON Job_Master.Job_ID = Job_College.Job_Id
INNER JOIN
Job_Branch ON Job_Branch.Job_College_ID = Job_College.Job_College_ID
WHERE
Job_Branch.Branch_Id = #branch
Logically query is correct and is producing expected results if I pass value of #branch directly to it (instead of using SELECT query in the beginning) but with this method shown in code by populating value of #branch with select query at the beginning, this query results 0 results although #branch is receiving value from it.
Things I have tried
Using SELECT query directly inside WHERE clause
I have seen that PRINT #branch gives right output so it should work in WHERE clause but it is not working
Please help!! I am stuck for past many hours

Related

Inner-join errors on SQL stored procedure

I guess I'm just a bit weary, but the answer isn't jumping out for me right now. It throws an error
SQL Error [4145] [S0001]: An expression of non-boolean type specified in a context where a condition is expected, near 'WHERE'
Code:
ALTER PROCEDURE [dbo].[spDirectoryLookup]
(#LookupName nvarchar(100),
#ClientNumber bigint)
AS
BEGIN
SELECT DISTINCT
DL.subid,
C.ClientNumber,
C.ClientName,
listid,
dld.Description
FROM
INTELLIGENT_2414_1.DBO.dirListings DL
INNER JOIN
Intelligent_2414_1.DBO.cltClients C ON DL.subid = C.subid
INNER JOIN
INTELLIGENT_2414_1.DBO.dirListingDescriptions dld ON dld.listID
WHERE
Description LIKE '%'+#LookupName+'%'
AND DL.subid IN (SELECT subid
FROM Intelligent_2414_1.DBO.cltClients C
WHERE C.ClientNumber = #ClientNumber)
END
The second join's on clause is missing a condition. I'm guessing you meant to do this:
INNER JOIN INTELLIGENT_2414_1.DBO.dirListingDescriptions dld on dld.listID = DL.listid

Multiple columns in aggregated expression

I've been struggling with an elegant solution for this for a while, and thought I'd finally cracked it but am now getting the error
Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.
Which is frustrating me!
In essence the query is:
select
u.username + ' ' + u.surname,
CASE WHEN ugt.type = 'Contract'
THEN
(
select sum(dbo.GET_INVOICE_WEEKLY_AVERAGE_VALUE(pc.placementid, u.UserId))
from PlacementConsultants pc
where pc.UserId = u.UserId
and pc.CommissionPerc >= 80
)
END
from usergradetypes ugt
inner join usergrades ug on ug.gradeid = ugt.usergradetypeid
inner join users u on u.userid = ug.userid
The function GET_INVOICE_WEEKLY_AVERAGE_VALUE is as follows
ALTER function [dbo].[GET_INVOICE_WEEKLY_AVERAGE_VALUE]( #placementid INT, #userid INT )
RETURNS numeric(9,2)
AS
BEGIN
DECLARE #retVal numeric(9,2)
DECLARE #rollingweeks int
SET #rollingweeks = (select ISNULL(rollingweeks,26) FROM UserGradeTypes ugt
inner join UserGrades ug on ugt.UserGradeTypeID = ug.gradeid
WHERE ug.userid = #userid)
SELECT #retVal =
sum(dbo.GET_INVOICE_NET_VALUE(id.InvoiceId)) / #rollingweeks from PlacementInvoices pli
inner join invoicedetails id on id.invoiceid = pli.InvoiceId
where pli.PlacementID = #placementid
and pli.CreatedOn between DATEADD(ww,-#rollingweeks,getdate()) and GETDATE()
RETURN #retVal
The query runs fine without the sum but when I'm trying to sum the value of the deals, it's falling over (which I need to do for a summary page)
I do not know why this fails:
select sum(dbo.GET_INVOICE_WEEKLY_AVERAGE_VALUE(pc.placementid, u.UserId))
but this works:
select sum(dbo.GET_INVOICE_WEEKLY_AVERAGE_VALUE(pc.placementid, pc.UserId))
It is curious and seems like a bug to me.
The error message, though, suggests that all the columns inside the sum() need to come from either the outer referenced tables or the inner referenced tables, but not both. I don't understand the reason for this. My best guess is that mixing the two types of references confuses the optimizer.
I haven't seen this error message before, by the way.
EDIT:
It is very easy to reproduce, and does not require a function call:
with t as (select 1 as col)
select t.*,
(select sum(t2.col + t.col) from t t2) as newcol
from t;
Very interesting. I think this might violate the standard. The equivalent query does run on Oracle.

SQL query works in SQL Server, fails in Excel (Microsoft Query)

I have the following query which works as intended :
SELECT
SERVICE_HISTORY.ServiceMode, SERVICE_HISTORY.CreatedDate,
SERVICE_HISTORY.CreatedBy, SERVICE_HISTORY.Branch,
SERVICE_HISTORY.Comments
FROM
DEBA_US.dbo.SERVICE_HISTORY
JOIN
(SELECT MAX(SERVICE_HISTORY.CreatedDate) AS maxDate, CUSTOMER.AccNo
FROM DEBA_US.dbo.CUSTOMER
INNER JOIN (DEBA_US.dbo.SERVICE_HISTORY
INNER JOIN DEBA_US.dbo.CAR ON SERVICE_HISTORY.ROW_PK = CAR.ROW_PK) ON CUSTOMER.ROW_PK = CAR.ROW_PK
WHERE
CUSTOMER.AccNo LIKE 'CUS-1234'
AND CAR.DateSubmitted IS NULL
GROUP BY
CUSTOMER.AccNo) AS testQuery ON testQuery.maxDate = SERVICE_HISTORY.CreatedDate
The query is to gives me the latest (max) service history date for a given customer.
When I execute the query in SQL Server, it works perfectly fine, but when I put the same query into EXCEL 2010 (Microsoft Query) it give me the error:
No Column name was specified for Column 1 of 'testQuery'
Invalid column name 'maxDate'
Statement could not be prepared
I'm not able to fix the query to get pass the error. Can someone please tell me why Excel isn't working with the above query? Thanks
You need to put testQuery and maxDate inside single quotations
SELECT
SERVICE_HISTORY.ServiceMode, SERVICE_HISTORY.CreatedDate,
SERVICE_HISTORY.CreatedBy, SERVICE_HISTORY.Branch,
SERVICE_HISTORY.Comments
FROM
DEBA_US.dbo.SERVICE_HISTORY
JOIN
(SELECT MAX(SERVICE_HISTORY.CreatedDate) AS 'maxDate', CUSTOMER.AccNo
FROM DEBA_US.dbo.CUSTOMER
INNER JOIN (DEBA_US.dbo.SERVICE_HISTORY
INNER JOIN DEBA_US.dbo.CAR ON SERVICE_HISTORY.ROW_PK = CAR.ROW_PK) ON CUSTOMER.ROW_PK = CAR.ROW_PK
WHERE
CUSTOMER.AccNo LIKE 'CUS-1234'
AND CAR.DateSubmitted IS NULL
GROUP BY
CUSTOMER.AccNo) AS 'testQuery' ON testQuery.maxDate = SERVICE_HISTORY.CreatedDate
The only thing you need to do is to add square brackets around the maxDate like following:
SELECT
SERVICE_HISTORY.ServiceMode, SERVICE_HISTORY.CreatedDate,
SERVICE_HISTORY.CreatedBy, SERVICE_HISTORY.Branch,
SERVICE_HISTORY.Comments
FROM
DEBA_US.dbo.SERVICE_HISTORY
JOIN
(SELECT MAX(SERVICE_HISTORY.CreatedDate) AS [maxDate], CUSTOMER.AccNo
FROM DEBA_US.dbo.CUSTOMER
INNER JOIN (DEBA_US.dbo.SERVICE_HISTORY
INNER JOIN DEBA_US.dbo.CAR ON SERVICE_HISTORY.ROW_PK = CAR.ROW_PK) ON CUSTOMER.ROW_PK = CAR.ROW_PK
WHERE
CUSTOMER.AccNo LIKE 'CUS-1234'
AND CAR.DateSubmitted IS NULL
GROUP BY
CUSTOMER.AccNo) AS testQuery ON testQuery.maxDate = SERVICE_HISTORY.CreatedDate

Joining on one of Two Tables Based on Parameter

Not sure if this can be done, but here is what I am trying to do.
I have two tables:
Table 1 is called Task and it contains all of the possible Task Names
Table 2 is called Task_subset and it contains only a subset of the Task Names included in Table 1
I have a variable called #TaskControl, that is passed in as a parameter, it either is equal to Table1 or Table2
Based on the value of the #TaskControl variable I want to join one of my Task Tables
For example:
If #TaskControl = 'Table1':
Select * From Orders O Join Task T on T.id = O.id
If #TaskControl = 'Table2):
Select * From Orders O Join Task_subset T on T.id = O.id
How would I do this, Sql Server 08
Don't overcomplicate it. Put it into a stored proc like so:
CREATE PROCEDURE dbo.MyProcedure(#TaskControl varchar(20))
AS
If #TaskControl = 'Table1'
Select * From Orders O Join Task T on T.id = O.id
ELSE If #TaskControl = 'Table2'
Select * From Orders O Join Task_subset T on T.id = O.id
ELSE SELECT 'Invalid Parameter'
Or just straight TSQL with no proc:
If #TaskControl = 'Table1'
Select * From Orders O Join Task T on T.id = O.id
ELSE If #TaskControl = 'Table2'
Select * From Orders O Join Task_subset T on T.id = O.id
Doing it exactly as you do it right now is the best way. Having one single statement that attempts to somehow dynamically join one of two statements is the last thing you want. T-SQL is a language for data access, not for DRY code-reuse programming. If you attempt to have a single statement then the optimizer has to come up with a plan that always work, no matter the value of #TaskControl, and so the plan will always have to join both tables.
A more lengthy discussion on this topic is Dynamic Search Conditions in T-SQL (your dynamic join falls into the same topic as dynamic search).
If they are UNION compatible you could give this a shot. From a quick test this end it only appears to access the relevant table.
I do agree more with JNK's and Remus's answers however. This does have a recompilation cost for every invocation and not much benefit.
;WITH T AS
(
SELECT 'Table1' AS TaskControl, id
FROM Task
UNION ALL
SELECT 'Table2' AS TaskControl, id
FROM Task_subset
)
SELECT *
FROM T
JOIN Orders O on T.id = O.id
WHERE TaskControl = #TaskControl
OPTION (RECOMPILE)
I don't know how good performance would be, and this would not scale well as you add on additional optional tables, but this should work in the situation that you present.
SELECT
O.some_column,
COALESCE(T.some_task_column, TS.some_task_subset_column)
FROM
Orders O
LEFT OUTER JOIN Tasks T ON
#task_control = 'Tasks' AND
T.id = O.id
LEFT OUTER JOIN Task_Subsets TS ON
#task_control = 'Task Subsets' AND
TS.id = O.id
Try the following. It should avoid the stored procedure plan getting bound based on the value of the parameter passed during the first execution of the stored procedure (See SQL Server Parameter Sniffing for details):
create proc dbo.foo
#TaskControl varchar(32)
as
declare #selection varchar(32)
set #selection = #TaskControl
select *
from dbo.Orders t
join dbo.Task t1 on t1.id = t.id
where #selection = 'Table1'
UNION ALL
select *
from dbo.Orders t
join dbo.Task_subset t1 on t1.id = t.id
where #selection = 'Table2'
return 0
go
The stored procedure shouldn't get recompiled for each invocation, either, as #Martin suggested might happen, but the parameter value 1st passed in should not influence the execution plan the gets bound. But if performance is an issue, run a sql trace with the profiler and see if the cached execution plan is reused or if a recompile is triggered.
One thing, though: you will need to ensure, though, that each individual select in the UNION returns the exact same columns. Each select in a UNION must have the same number of columns and each column must have a common type (or default conversion to the common type). The 1st select defines the number, types and names of the columns in the result set.

SQL Stored Procedure Performance Fine on SQL2008 but Awful on SQL2005

I have a stored procedure that I have developed on a SQL2008 server that runs <1sec. On another server which is SQL2005 the same sp on the same database takes ~1minute. Without going into the details of the database schema can anyone see anything obvious in this SP that may cause this performance discrepancy? Could it be the use of the CTE? Is there an alternative?
EDIT - I have now noticed that if I run the SQL directly on SQL 2005 it runs in ~4secs but executing the SP still takes over a minute?? Looks like the problem may like in the SP execution??
CREATE PROCEDURE Workflow.GetTopTasks
-- Add the parameters for the stored procedure here
#ownerUserId int,
#topN int = 10
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET ROWCOUNT #topN;
-- Insert statements for procedure here
WITH cteCalculatedDate (MilestoneDateId, CalculatedMilestoneDate)
AS
(
-- Anchor member definition
SELECT md.MilestoneDateId, md.SpecifiedDate
FROM Workflow.MilestoneDate md
WHERE md.RelativeMilestoneDateId IS NULL
UNION ALL
-- Recursive member definition
SELECT md.MilestoneDateId, CalculatedMilestoneDate + md.RelativeDays
FROM Workflow.MilestoneDate md
INNER JOIN cteCalculatedDate cte
on md.RelativeMilestoneDateId = cte.MilestoneDateId
)
-- Statement that executes the CTE
select
we.*
from Workflow.WorkflowElement we
left outer join cteCalculatedDate cte
on cte.MilestoneDateId = we.DueDateId
inner join Workflow.WorkflowInstance wi
on wi.WorkflowInstanceId = we.WorkflowInstanceId
left outer join Workflow.SchemeWorkflow sw
on sw.WorkflowInstanceId = wi.WorkflowInstanceId
left outer join Workflow.Scheme s
on s.SchemeId = sw.SchemeId
inner join Workflow.WorkflowDefinition wd
on wd.WorkflowDefinitionId = wi.WorkflowDefinitionId
where
we.OwnerId = #ownerUserId -- for given owner
and we.CompletedDate is null -- is not completed
and we.ElementTypeId <= 4 -- is Action, Data, Decision or Document (Not End, Start or KeyDate)
and cte.CalculatedMilestoneDate is not null -- has a duedate
UNION
select
we.*
from Workflow.WorkflowElement we
left outer join cteCalculatedDate cte
on cte.MilestoneDateId = we.DueDateId
inner join Workflow.WorkflowInstance wi
on wi.WorkflowInstanceId = we.WorkflowInstanceId
left outer join Workflow.SchemeWorkflow sw
on sw.WorkflowInstanceId = wi.WorkflowInstanceId
left outer join Workflow.Scheme s
on s.SchemeId = sw.SchemeId
inner join Workflow.WorkflowDefinition wd
on wd.WorkflowDefinitionId = wi.WorkflowDefinitionId
where
we.OwnerId = #ownerUserId -- for given owner
and we.CompletedDate is null -- is not completed
and we.ElementTypeId <= 4 -- is Action, Data, Decision or Document (Not End, Start or KeyDate)
and cte.CalculatedMilestoneDate is null -- does NOT have a duedate
SET ROWCOUNT 0
END
EDIT - I have now noticed that if I
run the SQL directly on SQL 2005 it
runs in ~4secs but executing the SP
still takes over a minute??
Bad parameter sniffing then:
http://elegantcode.com/2008/05/17/sql-parameter-sniffing-and-what-to-do-about-it/
SQL poor stored procedure execution plan performance - parameter sniffing
Parameter sniffing was bad in 2005, but better in 2008.
You union is selecting CalculatedMilestoneDate equal to NULL and not equal to Null.
This is redundant, the entire UNION can be removed by just removing the condition on CalculatedMilestoneDate from the where clause.
Other than that, you should verify that both databases have the same indexes defined.
-- Statement that executes the CTE
select
we.*
from Workflow.WorkflowElement we
left outer join cteCalculatedDate cte
on cte.MilestoneDateId = we.DueDateId
inner join Workflow.WorkflowInstance wi
on wi.WorkflowInstanceId = we.WorkflowInstanceId
left outer join Workflow.SchemeWorkflow sw
on sw.WorkflowInstanceId = wi.WorkflowInstanceId
left outer join Workflow.Scheme s
on s.SchemeId = sw.SchemeId
inner join Workflow.WorkflowDefinition wd
on wd.WorkflowDefinitionId = wi.WorkflowDefinitionId
where
we.OwnerId = #ownerUserId -- for given owner
and we.CompletedDate is null -- is not completed
and we.ElementTypeId <= 4 -- is Action, Data, Decision or Document (Not End, Start or KeyDate)
If the schemas match then perhaps you are missing important indexes in the sql server 2005 instance. Try running the sql server tuning advisors and applying its index recommendations.