I have a complex stored procedure that will also call other stored procedures as part of the workflow. I have checked all stored procedures for the ambiguous column 'ColumnId' error.
The first oddity is that the error is paramaterized with a single input and the error will not recreate for all users even with the same input. The second oddity is that I have checked all the SELECT, JOIN, WHERE, ORDER BY, and GROUP BY for the normal errors of ambiguity and not found any violations.
The only potential violation might be
SELECT RateID
FROM Rate.tblRate
INNER JOIN #tmpRate
ON tblRate.CustomerID = #tmpRate.CustomerID
Could the line for ON be an issue as it is not
ON Rate.tblRate.CustomerID = #tmpRate.CustomerID
In your case, the proc could return different, or multiple result sets making this behavior sporadic. However,I've seen this for temp tables a lot, though I can't explain why. If you alias that table, it resolves it every time.
SELECT RateID
FROM Rate.tblRate r
INNER JOIN #tmpRate t
ON r.CustomerID = t.CustomerID
This is good practice as it is required for other instances, like table variables.
if object_id('tempdb..#temp') is not null drop table #temp
select 1 as ID into #temp
declare #table table (ID int)
insert into #table
values
(1)
select *
from
#table
inner join #temp on #temp.ID = #table.ID
This will throw the error:
Must declare the scalar variable "#table".
So, alias it and it will work:
select *
from
#table t
inner join #temp on #temp.ID = t.ID
There are a lot of blogs out there on why it's a good habit to pick up. Here is one.
Related
I have a recursive function which gives allows me to give any GUID in the heirarchy and it pulls back all the values below it. This is used for folder security.
ALTER FUNCTION dbo.ValidSiteClass
(
#GUID UNIQUEIDENTIFIER
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
WITH previous
AS ( SELECT
PK_SiteClass,
FK_Species_SiteClass,
CK_ParentClass,
ClassID,
ClassName,
Description,
SyncKey,
SyncState
FROM
dbo.SiteClass
WHERE
PK_SiteClass = #GUID
UNION ALL
SELECT
Cur.PK_SiteClass,
Cur.FK_Species_SiteClass,
Cur.CK_ParentClass,
Cur.ClassID,
Cur.ClassName,
Cur.Description,
Cur.SyncKey,
Cur.SyncState
FROM
dbo.SiteClass Cur,
previous
WHERE
Cur.CK_ParentClass = previous.PK_SiteClass)
SELECT DISTINCT
previous.PK_SiteClass,
previous.FK_Species_SiteClass,
previous.CK_ParentClass,
previous.ClassID,
previous.ClassName,
previous.Description,
previous.SyncKey,
previous.syncState
FROM
previous
)
I have a stored procudure which then later needs to figure out what folders have changed in the user's heirarchy which I use for change tracking. When I try to join it with my change tracking it never returns the query. For example, the following doesn't ever return any results (It just spins, I stop it after 6 minutes)
DECLARE #ChangeTrackerNumber INT = 13;
DECLARE #SelectedSchema UNIQUEIDENTIFIER = '36EC6589-8297-4A82-86C3-E6AAECCC7D95';
WITH validones AS (SELECT PK_SITECLASS FROM ValidSiteClass(#SelectedSchema))
SELECT SiteClass.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(SYS_CHANGE_OPERATION) ChangeOp
FROM dbo.SiteClass
INNER JOIN CHANGETABLE(CHANGES SiteClass, #ChangeTrackerNumber) tracking --tracking
ON tracking.PK_SiteClass = SiteClass.PK_SiteClass
INNER JOIN validones
ON SiteClass.PK_SiteClass = validones.PK_SiteClass
WHERE SyncState IN ( 0, 2, 4 );
The only way I can make this work is with a temptable such as:
DECLARE #ChangeTrackerNumber INT = 13;
DECLARE #SelectedSchema UNIQUEIDENTIFIER = '36EC6589-8297-4A82-86C3-E6AAECCC7D95';
CREATE TABLE #temptable
(
[PK_SiteClass] UNIQUEIDENTIFIER
);
INSERT INTO #temptable
(
PK_SiteClass
)
SELECT PK_SiteClass
FROM dbo.ValidSiteClass(#SelectedSchema);
SELECT SiteClass.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(SYS_CHANGE_OPERATION) ChangeOp
FROM dbo.SiteClass
INNER JOIN CHANGETABLE(CHANGES SiteClass, #ChangeTrackerNumber) tracking --tracking
ON tracking.PK_SiteClass = SiteClass.PK_SiteClass
INNER JOIN #temptable
ON SiteClass.PK_SiteClass = #temptable.PK_SiteClass
WHERE SyncState IN ( 0, 2, 4 );
DROP TABLE #temptable;
In other words, the CTE doesn't work and I need to call the temptable.
First question, isn't the CTE supposed to be the same thing (but better) than a temptable?
Second question, does anyone know why this could be so? I have tried inner joins and using a where and in clause also. Is there something different about a recursive query that might cause this odd behavior?
Generally, when you have a table-valued function, you'd just include it like it was a regular table (assuming you have a parameter to pass to it). If you want to pass a series of parameters to it, you'd use outer apply, but that doesn't seem to be the case here.
I think (maybe) this is more like you want (notice no with clause):
select
s.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(t.SYS_CHANGE_OPERATION) ChangeOp
from
dbo.ValidSiteClass(#SelectedSchema) v
inner join
SiteClass s
on
s.PK_SiteClass = v.PK_SiteClass
inner join
changetable(changes SiteClass, #ChangeTrackerNumber) c
on
c.PK_SiteClass = s.PK_SiteClass
where
SyncState in ( 0, 2, 4 )
option (force order)
...which I'll admit doesn't look that mechanically different than what you have with the with clause. However, you could be running into an issue with SQL Server just picking a horrible plan not having any other clues. Including the option (force order) makes SQL Server perform the joins according to the order you put them in...and sometimes this makes an incredible difference.
I wouldn't say this is recommended. In fact, it's a hack...just to see WTF. But, play around with the order...and get SQL Server to show you the actual execution plans to see why it might have come up with something so heinous. An inline table-valued function is visible to SQL Server's query plan engine, and it may decide to not treat the function as an isolated thing the way programmers traditionally think about functions. I suspect this is why it took so long to begin with.
Funny enough, if your function were to be a so-called multi-lined table-valued function, SQL would definitely not have the same type of visibility into it when planning this query...and it might run faster. Again, not a recommendation, just something that might hack a better plan.
I have common clause in Most of the Procedures like
Select * from TABLE A + Joins where <Conditions>
And
(
-- All Broker
('True' = (Select AllBrokers from SiteUser where ID = #SiteUserID))
OR
(
A.BrokerID in
(
Select BrokerID from SiteUserBroker where SiteUserID
= #SiteUserID)
)
)
So basically if the user has access to all brokers the whole filter should not be applied else if should get the list of Broker
I am bit worries about the performance as this is used in lot of procedures and data has started reaching over 100,000 records and will grow soon, so can this be better written?
ANY Ideas are highly appreciated
One of the techniques is to use built dynamic T-SQL statement and then execute it. Since, this is done in stored procedure you are OK and the idea is simple.
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
SET #DynamicTSQLStatement = 'base query';
IF 'Getting All Brokers is not allowed '
BEGIN;
SET #DynamicTSQLStatement = #DynamicTSQLStatement + 'aditional where clause'
END;
EXEC sp_executesql #DynamicTSQLStatement;
Or instead of using dynamic T-SQL statement you can have two separate queries - one for users seeing all the data and one for users seeing part of the data. This can lead to code duplication.
Another way, is to turn this OR statement in INNER JOIN. You should test the performance in order to be sure you are getting something from it. The idea is to create a temporary table (it can have primary key or indexes if needed) and store all visible broker ids there - if the users sees all, then Select BrokerID from SiteUserBroker and if the users sees a few - Select BrokerID from SiteUserBroker where SiteUserID = #SiteUserID. In the second way, you are going to simplify the whole statement, but be sure to test if performance is improved.
CREATE TABLE #SiteUserBroker
(
[BrokerID] INT PRIMARY KEY
);
INSERT INTO #SiteUserBroker ([BrokerID])
SELECT BrokerID
FROM SiteUserBroker
where SiteUserID = #SiteUserID
OR ('True' = (Select AllBrokers from SiteUser where ID = #SiteUserID));
Select *
from TABLE A
INNER JOIN #SiteUserBroker B
ON A.BrokerID = B.[BrokerID]
-- other joins
where <Conditions>
As we are using INNER JOIN you can add it at the begging. If there are LEFT JOINs after it, it will affect the performance in positive way.
Adding to #gotqn answer, you can add EXISTS instead of IN (Note - This is not complete answer) -
AND EXISTS (
Select 1/0 from SiteUserBroker X
where A.BrokerID = X.BrokerID AND
X.SiteUserID = #SiteUserID
)
I found that Exists performs better than In in some cases. Please verify your case.
I have tried lots of permutations of DECLARE and CREATE but I can't display any data from my temporary table. Here's the latest iteration:
DECLARE GLOBAL TEMPORARY TABLE session.temporary_table (ACTNO CHAR);
INSERT INTO session.temporary_table VALUES ('1'), ('2');
SELECT * FROM session.temporary_table;
SELECT DISTINCT ACTKWD FROM JMILLER.ACT INNER JOIN session.temporary_table ON ACT.ACTNO = session.temporary_table.ACTNO;
DROP TABLE session.temporary_table;
The first SELECT is to see if there's anything in the temporary_table. Which there doesn't seem to be.
Even though when I test the query in this tool it says every line of it completed correctly.
What am I doing wrong in my sql statements? I was having trouble with permissions earlier but that seems to be fine now and I'm not getting any errors, so it must be in my code.
You did not add the clause ON COMMIT PRESERVE ROWS.
Also see if you could make things simpler by using CTE (the WITH statement)
Modify to
SELECT DISTINCT ACTKWD FROM JMILLER.ACT t0 INNER JOIN session.temporary_table t1 ON t0.ACTNO = t1.ACTNO;
I currently have a performance issue with a query (that is more complicated than the example below). Originally the query would run and take say 30 seconds, then when I switched out the use of a table variable to using a temp table instead, the speed is cut down to a few seconds.
Here is a trimmed down version using a table variable:
-- Store XML into tables for use in query
DECLARE #tCodes TABLE([Code] VARCHAR(100))
INSERT INTO
#tCodes
SELECT
ParamValues.ID.value('.','VARCHAR(100)') AS 'Code'
FROM
#xmlCodes.nodes('/ArrayOfString/string') AS ParamValues(ID)
SELECT
'SummedValue' = SUM(ot.[Value])
FROM
[SomeTable] st (NOLOCK)
JOIN
[OtherTable] ot (NOLOCK)
ON ot.[SomeTableID] = st.[ID]
WHERE
ot.[CodeID] IN (SELECT [Code] FROM #tCodes) AND
st.[Status] = 'ACTIVE' AND
YEAR(ot.[SomeDate]) = 2013 AND
LEFT(st.[Identifier], 11) = #sIdentifier
Here is the version with the temp table which performs MUCH faster:
SELECT
ParamValues.ID.value('.','VARCHAR(100)') AS 'Code'
INTO
#tCodes
FROM
#xmlCodes.nodes('/ArrayOfString/string') AS ParamValues(ID)
SELECT
'SummedValue' = SUM(ot.[Value])
FROM
[SomeTable] st (NOLOCK)
JOIN
[OtherTable] ot (NOLOCK)
ON ot.[SomeTableID] = st.[ID]
WHERE
ot.[CodeID] IN (SELECT [Code] FROM #tCodes) AND
st.[Status] = 'ACTIVE' AND
YEAR(ot.[SomeDate]) = 2013 AND
LEFT(st.[Identifier], 11) = #sIdentifier
The problem I have with performance is solved with the change but I just don't understand why it fixes the issue and would prefer to know why. It could be related to something else in the query but all I have changed in the stored proc (which is much more complicated) is to switch from using a table variable to using a temp table. Any thoughts?
The differences and similarities between table variables and #temp tables are looked at in depth in my answer here.
Regarding the two queries you have shown (unindexed table variable vs unindexed temp table) three possibilities spring to mind.
INSERT ... SELECT to table variables is always serial. The SELECT can be parallelised for temp tables.
Temp tables can have column statistics histograms auto created for them.
Usually the cardinality of table variables is assumed to be 0 (when they are compiled when the table is empty)
From the code you have shown (3) seems the most likely explanation.
This can be resolved by using OPTION (RECOMPILE) to recompile the statement after the table variable has been populated.
I have the code below that does not return any values when it should. Is there anyone that can find anything faulty with it? When I run the script I get 0 values in return (0 rows affected), even though I know that there are rows that should be selected.
This is just a part of the whole script, and when I run everything I get the error message "Warning: Null value is eliminated by an aggregate or other SET operation". But I don´t know what this implies for my script. I only have 1 "group by" in the script (which I don´t post here since that coding works). Does anyone know?
The code, below, should create a temp-table and to the temp-table insert rows from the table "StatusHistorik" where the following conditions are met:
1) [NyStatus]=4 (NyStatus is a column from the table "StatusHistorik"),
2) The variable "EnhetsId" should not exist in the table "SKL_AdminKontroll_SkaÅtgärdas" (F). Code: "F.EnhetsId is null".
I have used inner joins in both cases. This should result in a number of observations/rows in the temp-table, but returns nothing. Does anyone find any errors with the coding that could explain the absent of result?
I should mention that when I "comment away" the last inner join and the last part of the where-statement, the script works as it is supposed to. So I suspect that it is the last inner join-statement that is wrong somehow.
declare #temp2 table (
EnhetsId varchar(50),
TjanstId Int,
Tabell varchar(50),
Kommentar ntext,
Uppdaterad datetime
);
WITH ENHET_AVSLUT AS
(
SELECT DISTINCT A.[EnhetsId]
FROM [StatistikinlamningDataSKL].[dbo].[StatusHistorik] A
inner join (
select [EnhetsId], max(SenastUppdaterad) as SenastDatum
from [StatistikinlamningDataSKL].[dbo].[StatusHistorik]
group by [EnhetsId]
) B
on A.[EnhetsId] = B.[EnhetsId] and A.[SenastUppdaterad] = B.SenastDatum
INNER JOIN
StatistikinlamningDataSKL.dbo.SKL_AdminKontroll_SkaÅtgärdas F ON A.EnhetsId = F.EnhetsId
WHERE [NyStatus] = 4 AND F.EnhetsId is null
)
insert into #temp2
(EnhetsId, TjanstId, Tabell, Kommentar, Uppdaterad)
SELECT
EnhetsId, 1, ''GR_PS09_1'', ''OK'', getdate()
from ENHET_AVSLUT
select * from #temp2
Best regards,
Hannes
Because you are saying
The variable "EnhetsId" should not exist in the table "SKL_AdminKontroll_SkaÅtgärdas" (F). Code: "F.EnhetsId is null".
an INNER JOIN will try to join the tables and will not be able to perform a JOIN on NULL values, so the result will be zero rows.
So you must try using LEFT JOIN, where the tables ON LEFT will be pulled irrespective of the JOIN condition being satisfied.
I must warn you though, if the table is large it might have performance issues.
Your inner SQL must be something like
SELECT DISTINCT A.[EnhetsId]
FROM [StatistikinlamningDataSKL].[dbo].[StatusHistorik] A
inner join (
select [EnhetsId], max(SenastUppdaterad) as SenastDatum
from [StatistikinlamningDataSKL].[dbo].[StatusHistorik]
group by [EnhetsId]
) B
on A.[EnhetsId] = B.[EnhetsId] and A.[SenastUppdaterad] = B.SenastDatum
LEFT JOIN
StatistikinlamningDataSKL.dbo.SKL_AdminKontroll_SkaÅtgärdas F ON A.EnhetsId = F.EnhetsId
WHERE [NyStatus] = 4