Query hangs using parameter in WHERE clause - sql-server

I am having difficulty understanding a query here.
My query ultimately returns a result set in a report where you can specify a Person in the parameter to view their data.
I am joining my Dimension table to my Employee table to return the Name from the Employee table. It looks something like this
Declare #PM varchar(30)
Set #PM = 'John Smith'
SELECT....FullName, EmployeeID, ....
FROM...
Inner Join EmployeesT on emp.EmployeeNumber = DimP.PersonID
WHERE FullName in (#PM)
Note: My Employee table is in nvarchar and Dimension is is varchar but i dont think that matters as the join still works.
Now, I set a parameter up at the top for testing.
Here is my issue: If I switch the WHERE clause to say WHERE DimP.PersonID IN ('12345') my query takes 3 seconds to run. When I change the query to WHERE FullName in (#PM) the query is taking forever to run; it hangs and runs for 5+ minuets. Has anyone experienced a similar issue?
The result set produces the correct data with multiple "people" and I want to test this by specifying a person, not an id, but when I change the parameter to the Name the query hangs..

It might be parameter sniffing. It is hanging onto the optimized execution plan for another parameter value.
Try using OPTION (OPTIMIZE FOR (#parm UNKNOWN)) at the end of your query.
Declare #PM varchar(30)
Set #PM = 'John Smith'
SELECT....FullName, EmployeeID, ....
FROM...
Inner Join EmployeesT on emp.EmployeeNumber = DimP.PersonID
WHERE FullName in (#PM)
OPTION (OPTIMIZE FOR (#PM UNKNOWN))

My coworker was able to solve this : she used `WHERE emp.empNumber = (CASE WHEN emp.FullName IN (#PM) )
:)

Related

SSRS multiple single cells

I am trying to figure out best way to add multiple fields to a SSRS report.
Report has some plots and tablix which are populated from queries but now I have been asked to add a table with ~20 values. The problem is that I need to have them in a specific order/layout (that I cannot obtain by sorting) and they might need to have a description added above which will be static text (not from the DB).
I would like to avoid situation where I keep 20 copy of the same query which returns single cell where the only difference would be in:
WHERE myTable.partID = xxxx
Any chance I could keep a single query which takes that string like a parameter which I could specify somehow via expression or by any other means?
Not a classical SSRS parameter as I need a different one for each cell...
Or will I need to create 20 queries to fetch all those single values and then put them as separate textfields on the report?
When I've done this in the past, I build a single query that gets all the data I need with some kind of key.
For example I might have a list of captions and values, one per row, that I need to display as part of a report page. The dataset query might look something like ...
DECLARE #t TABLE(Key varchar(20), Amount float, Caption varchar(100))
INSERT INTO #t
SELECT 'TotalSales', SUM(Amount), NULL AS Amount FROM myTable WHERE CountryID = #CountryID
UNION
SELECT 'Currency', NULL, CurrencyCode FROM myCurrencyTable WHERE CountryID = #CountryID
UNION
SELECT 'Population', Population, NULL FROM myPopualtionTable WHERE CountryID = #CountryID
SELECT * FROM #t
The resulting dataset would look like this.
Key Amount Caption
'TotalSales' 12345 NULL
'Currency' NULL 'GBP'
'Population' 62.3 NULL
Lets say we call this dataset dsStuff then in each cell/textbox the xpression would simply be something like.
=LOOKUP("Population", Fields!Key.Value, Fields!Amount.Value, "dsStuff")
or
=LOOKUP("Currency", Fields!Key.Value, Fields!Caption.Value, "dsStuff")

Understanding an ambiguous column name for inner query

I ran into a weird query today that I thought would be failed, but it succeeded in an unexpected way. Here's a minimal reproduction of it.
Tables and data:
CREATE TABLE Employee(ID int, Name varchar(max))
CREATE TABLE Engineer(ID int, Title varchar(max))
GO
INSERT INTO Employee(ID, Name) VALUES (1, 'Bobby')
INSERT INTO Employee(ID, Name) VALUES (2, 'Sue')
INSERT INTO Engineer(ID, Title) VALUES (1, 'Electrical Engineer')
INSERT INTO Engineer(ID, Title) VALUES (2, 'Network Engineer')
Queries:
--Find all Engineers with same title as Bobby has
SELECT * FROM Engineer WHERE Title IN (select Title from Employee WHERE Name = 'Bobby')
This returns all rows in Engineer table (unexpected, I thought it would fail). Note that the above query is incorrect. The inner query uses a column "Title" which doesn't exist in the table being selected from ("Employee"). So it must be binding the Title column value from Engineer in the outer query....which is always equal to itself so all rows are returned I think.
I can force it too if I fully qualify the column name, and that would fail as expected:
SELECT * FROM Engineer WHERE Title IN
(select Empl.Title from Employee Empl WHERE Name = 'Bobby')
This fails with "Invalid column name 'Title'."
Apparently if I were to add the Title column to the Employee table, it uses the Employee.Title column value instead.
ALTER TABLE Employee ADD Title varchar(max)
GO
UPDATE Employee SET Title = 'Electrical Engineer' WHERE ID = 1
UPDATE Employee SET Title = 'Network Engineer' WHERE ID = 2
SELECT * FROM Engineer WHERE Title IN
(select Title from Employee WHERE Name = 'Bobby')
This returns just one row (as expected).
I kind of understand what is happening here, what I'm looking for is a link to some documentation or some keyword that would help me read up and understand it fully (or even some explanation).
Of course it fails. There is no column named Title in your Employee table. In the query that does work it is a subquery so it is pulling Title from Engineer.
You can avoid this entirely if you develop the habit of ALWAYS referencing columns with 2 part naming instead of just the column name.
But in your queries you should start learning how to use joins instead of subqueries for everything. Your code would be far less confusing.
Since Title is not qualified it uses the Title from table Engineer
SELECT * FROM Engineer WHERE Title IN (select Title from Employee WHERE Name = 'Bobby')
In the last it uses the closest Title (from Employee) .
If you use alias and 2 part name then you stay out of this confusion.
As far as documentation. Finding closest column is probably an undocumented feature.
I found the documentation on the behavior: Qualifying Column Names in Subqueries
The general rule is that column names in a statement are implicitly qualified by the table referenced in the FROM clause at the same level. If a column does not exist in the table referenced in the FROM clause of a subquery, it is implicitly qualified by the table referenced in the FROM clause of the outer query.

SSRS avoid WHERE clause if select all is selected

I am working on an SSRS report and a part of my sql query is like
WHERE SuperVisorId IN (#SupervisorIDs) AND CreatedDate> #StartDate
where the #SupervisorIDs is a dropdown with option of "select all" and individual supervisors.
So if the supervisors "all" option is selected , then I don't need to include that in the where clause and my where clause is only this
WHERE CreatedDate> #StartDate
So how can I make the WHERE clause looks different according to Selection of dropdown?
This only applies if you are using a single valued parameter with a manually added All option to the list of available values. Multi-value parameters do not know when all options are selected.
SQL Server doesn't always execute the conditions in a where clause in the order you write them, so if you are using where (#p = 'all' or col = #p) and ... you may still be comparing your values.
If performance is a concern, you can avoid this by using a short circuiting case, that only progresses to the actual data comparison if it is necessary:
where case when #SupervisorIDs = 'All' then 1
else case when SuperVisorId = #SupervisorIDs then 1
else 0
end
end = 1
and CreatedDate > #StartDate
Assuming that you are using a dataset query to populate the supervisor parameter dropdown, then you can try this.
Create an additional hidden parameter of a boolean type. For this example, I'll call it #AllSupsSelected. Set the default value of the parameter to:
=COUNT(Parameters!SupervisorIds.Label)=COUNT(Fields!SupervisorIdLabel.Value,"SupervisorDataset")
Replace the field and dataset names accordingly. If the dataset is returning non-distinct values, you may have to tinker further to get this working.
Now your query can read:
WHERE #AllSupsSelected OR SupervisorId IN (#SupervisorIds)
Make your where clause like below
WHERE (
(SuperVisorId IN (#SupervisorIDs))
OR (
#SupervisorIDs = 0
AND COLUMN_WITH_NULL IS NOT NULL
)
)
AND CreatedDate > #StartDate
And pass 0 when selected "select all"
As an actual answer to your particular problem, set your multi-valued parameter dataset up similar to this to return all Supervisors as well as a value at the bottom of the list for No Supervisor:
select distinct SupervisorID as Value
,SupervisorName as Label
,1 as Sort
from Suppliers
union all
select <Uniquely identifiable value with the same data type as SupervisorID> as Value
,'No Supervisor' as Label
,2 as Sort
order by Sort
,Label
Then in your dataset set up your filtering similar to the below. I have structured it in this manner to avoid using the isnull function on your SupervisorID column, which will hurt the query performance:
select cols
from tables
where SupervisorID in(#SupervisorID)
or (SupervisorID is null
and <Unique value from above> in (#SupervisorID)
)
which version of ssrs ? in 2016, you don't need to alter your query. when you click "select all" by default it pass all the values. so your query works good without changing anything.
thanks,
SK

how do i ensure a select statement returns null values for specific columns?

This might sound kind of weird, but I have a query that joins two tables. I'm using an IF statements that dictates what to return. One path runs the query/join as is, the other needs to return all of the data from the first column, but only return column names with null values. Here's the query i have now:
declare #Date DATE = '06/07/2012'
IF #DATE >= GETDATE()
BEGIN
SELECT DisplayName, '' [RegularHours], ''[OvertimeHours]
FROM Sites
ORDER BY DisplayName
END
ELSE
SELECT sites.DisplayName, hrs.SiteDate, hrs.RegularHrs, hrs.OverTimeHrs
FROM Sites sites
left join SiteHours hrs on sites.SiteID = hrs.SiteID
ORDER BY DisplayName
What's making me nervous is that the second and third columns do not have values at all, not even NULL. I'm worried that this will pose a problem later. Any ideas?
If I understand the question correctly, I think you can do:
SELECT DisplayName, NULL as 'RegularHours', NULL as 'OvertimeHours'

Need help with Sql Server Full Text Search problem

I have a Full Text Catalog on single table, with three fields defined :-
TABLE: Animals
Fields: Name, Breed, LatinName.
Now, the Catalog seems to be working perfectly.
eg.
CREATE FUNCTION AnimalSearch
(
#Name NVARCHAR(200)
) RETURNS TABLE AS
RETURN
(
SELECT KEY_TBL.[Key] as Name,
KEY_TBL.RANK as Relevance
FROM CONTAINSTABLE(Animals, Name, #Name) AS KEY_TBL
)
Now, when i run this, i get the following results :-
Name = ma (no results)
Name = mat (no results)
Name = matt (1 result - correct).
SELECT * FROM [dbo].[AnimalSearch]('ma')
Is this the correct way to use this? I've also tried replacing CONTAINSTABLE with FREETEXTTABLE .. same thing .. no results.
Any ideas, anyone?
Edit
I understand that this could be achieved in a stored proc. I'm was hoping to do this as a Table-Valued Function, so i could use this in some Linq2Sql. If it's really unperformant, then please say so.
Not sure it's a good idea. Table valued functions do not store statistics, so performance may suffer.

Resources