CTE Invalid column name - sql

I am completely stuck on this one. I looked in the other questions, but could not find one that answered this (that I could understand, anyway). I have the following CTE in my query but MaxUserID is squiggled red in the 3 places it's used with an "invalid column name 'MaxUserID'" error. The column it should represent is an int, if that helps. Any advice?
I'm using SQL Server 2008.
;with TotalCount(TotalCount,MaxUserID)
as
(
Select ISNULL(count(distinct uCPR.HeaderID), 0) as TotalCount, MaxUserID
from ClientFeedback.dbo.UnitCountCPR uCPR
where
uCPR.DHDate between #StartDate and #EndDateMod
and uCPR.TargetID in (#StatusID)
and uCPR.UserID = MaxUserID
and uCPR.DTStamp between #StartDate and #EndDateMod
and uCPR.ClientID in (#ClientID)
group by MaxUserID
)

Refresh your intellisense cache, and then use your table alias to pick the coluumns. Also, count will never be null, your IsNull isn't doing you any good. And is there a reason you are using in instead of equals?

Related

SQL Server : JOIN and WHERE

I'm trying to create a new table based on particular values that match between two tables and that works fine but my issue comes about when I try to filter the newly joined table by dates.
CREATE TABLE JoinedValuesTable
(
[Ref] INT IDENTITY(1,1) PRIMARY KEY,
[Parties] CHAR(50),
[Accounts] CHAR(50),
[Amount] FLOAT
);
The table above is created okay and I join insert values into it by joining two tables like this....
INSERT INTO JoinedValuesTable ([Parties], [Accounts], [Amount])
SELECT
InputPerson.[PARTY], Input_Y.[R_Account_1], InputPerson.[Amount]
FROM
InputPerson
JOIN
Input_Y ON InputPerson.[Action] = Input_Y.[Action]
And this works fine it's when I try to filter by dates that it doesn't seem to work....
INSERT INTO JoinedValuesTable([Parties], [Accounts], [Amount])
SELECT
InputPerson.[PARTY], Input_Y.[R_Account_1], InputPerson.[Amount]
FROM
InputPerson
JOIN
Input_Y ON InputPerson.[Action] = Input_Y.[Action]
WHERE
InputPerson.[Date] BETWEEN '2018-01-01' AND '2018-03-03'
I'm not getting any values into my new table. Anyone got any ideas?
Do not use between for dates. A better method is:
WHERE InputPerson.[Date] >= '2018-01-01' AND
InputPerson.[Date] < '2018-03-04'
I strongly recommend Aaron Bertrand's blog on this topic: What do BETWEEN and the devil have in common?
This assumes that Date is being stored as a date/time column. If it is a string, then you need to convert it to a date using the appropriate conversion function.
A shameless copy/paste from an earlier answer:
BETWEEN CONVERT(datetime,'2018-01-01') AND CONVERT(datetime,'2018-03-03')
The original answer: Datetime BETWEEN statement not working in SQL Server
I suspect that the datetime versus string is the culprit. Resulting in (without the insert into):
SELECT InputPerson.[PARTY], Input_Y.[R_1], Input_Y.[R_Account_1], InputPerson.[Amount]
FROM InputPerson
JOIN Input_Y ON InputPerson.[Action] = Input_Y.[Action]
WHERE InputPerson.[Date] BETWEEN CONVERT(datetime,'2018-01-01') AND CONVERT(datetime,'2018-03-03')
-- Edit (after comments from Olivier) --
Are you sure the select-statement returns results? Perhaps the Inner-Join combined with the Where-clause results in an empty result set.

Passing in parameter to where clause using IS NULL or Coalesce

I would like to pass in a parameter #CompanyID into a where clause to filter results. But sometimes this value may be null so I want all records to be returned. I have found two ways of doing this, but am not sure which one is the safest.
Version 1
SELECT ProductName, CompanyID
FROM Products
WHERE (#CompanyID IS NULL OR CompanyID = #CompanyID)
Version 2
SELECT ProductName, CompanyID
FROM Products
WHERE CompanyID = COALESCE(#CompanyID, CompanyID)
I have found that the first version is the quickest, but I have also found in other tables using a similar method that I get different result sets back. I don't quite understand the different between the two.
Can anyone please explain?
Well, both queries are handling the same two scenarios -
In one scenario #CompanyID contains a value,
and in the second #CompanyID contains NULL.
For both queries, the first scenario will return the same result set - since
if #CompanyId contains a value, both will return all rows where companyId = #CompanyId, however the first query might return it faster (more on that at the end of my answer).
The second scenario, however, is where the queries starts to behave differently.
First, this is why you get different result sets:
Difference in result sets
Version 1
WHERE (#CompanyID IS NULL OR CompanyID = #CompanyID)
When #CompanyID is null, the where clause will not filter out any rows whatsoever, and all the records in the table will be returned.
Version 2
WHERE CompanyID = COALESCE(#CompanyID, CompanyID)
When #CompanyID is null, the where clause will filter out all the rows where CompanyID is null, since the result of null = null is actually unknown - and any query with null = null as it's where clause will return no results, unless ANSI_NULLS is set to OFF (which you really should not do since it's deprecated).
Index usage
You might get faster results from the first version, since the use of any function on a column in the where clause will prevent SQL Server from using any index that you might have on this column.
You can read more about it on this article in MSSql Tips.
Conclusion
Version 1 is better than version 2.
Even if you do not want to return records where companyId is null it's still better to write as WHERE (#CompanyID IS NULL OR CompanyID = #CompanyID) AND CompanyID IS NOT NULL than to use the second version.
It's worth noting that using the syntax ([Column] = #Value OR [Column] IS NULL) is a much better idea than using ISNULL([Column],#Value) = #Value (or using COALESCE).
This is because using the function causes the query to become un-SARGable; so indexes won't be used. The first expression is SARGable, and thus, will perform better.
Just adding this, as the OP states "I have found that the first version is the quickest", and wanted to elaborate why (even though, currently the statement is incomplete, I am guessing this was more due to user error and ignorance).
The second version is not correct SQL (for SQL Server). It needs an operator. Presumably:
SELECT ProductName, CompanyID
FROM Products
WHERE COALESCE(#CompanyID, CompanyID) = CompanyID;
The first version is correct as written. If you have an index on CompanyID, you might find this faster:
SELECT *
FROM Products
WHERE CompanyID = #CompanyID
UNION ALL
SELECT *
FROM Products
WHERE #CompanyID IS NULL;

Why Does One SQL Query Work and the Other Does Not?

Please disregard the obvious problems with the manipulation of data in the where clause. I know! I'm working on it. While working on it, though, I discovered that this query runs:
SELECT *
FROM PatientDistribution
WHERE InvoiceNumber LIKE'PEX%'
AND ISNUMERIC(CheckNumber) = 1
AND CONVERT(BIGINT,CheckNumber) <> TransactionId
And this one does not:
SELECT *
FROM PatientDistribution
WHERE InvoiceNumber LIKE'PEX%'
AND CONVERT(BIGINT,CheckNumber) <> TransactionId
AND ISNUMERIC(CheckNumber) = 1
The only difference between the two queries is the order of items in the WHERE clause. I was under the impression that the SQL Server query optimizer would take the worry out of me having to worry about that.
The error returned is: Error converting data type varchar to bigint.
You are right, the order of the conditions shouldn't matter.
If AND ISNUMERIC(CheckNumber) = 1 is checked first and non-matching rows thus dismissed, then AND CONVERT(BIGINT,CheckNumber) <> TransactionId will work (for exceptions see scsimon's answer).
If AND CONVERT(BIGINT,CheckNumber) <> TransactionId is processed before AND ISNUMERIC(CheckNumber) = 1 then you may get an error.
That your first query worked and the second not was a matter of luck. It could just as well have been vice versa.
You can force one condition to be executed before the other:
SELECT *
FROM
(
SELECT *
FROM PatientDistribution
WHERE InvoiceNumber LIKE 'PEX%'
AND ISNUMERIC(CheckNumber) = 1
) num_only
WHERE CONVERT(BIGINT,CheckNumber) <> TransactionId;
You just got lucky that the first one worked, since you are correct that the order of what you list in the where clause does not matter. SQL is a declarative language meaning that you are telling the engine what should happen, not how. So your queries weren't executed with the same query plan I would suspect. Granted, you can affect what the optimizer does to a certain extent. You'll also notice this type of issue when using a CTE. For example:
declare #table table(columnName varchar(64))
insert into #table
values
('1')
,('1e4')
;with cte as(
select columnName
from #table
where isnumeric(columnName) = 1)
select
cast(columnName as decimal(32,16))
from cte
The above snippet you would assume that the second statement is ran on the results / subset from the CTE statement. However, you can't ensure this will happen and you could still get a type/conversion error on the second statement.
More importantly, you should know that ISNUMERIC() is largely misused. People often think that if it returns 1 then it could be converted to a decimal or int. But this isn't the case. It just checks that it's a valid numeric type. For example:
select
isnumeric('1e4')
,isnumeric('$')
,isnumeric('1,123,456')
As you can see, these evaluate to true, but would fail the conversion you put in your post.
Side note, your indexes are likely the reason why the first actually didn't error our.

SQL NOT IN failed

I am working on a query that will check the temp table if there is a record that do not exist on the main table. My query looks like this
SELECT * FROM [Telemarketing].[dbo].[PDCampaignBatch_temp]
WHERE [StartDateTime] NOT IN (SELECT [StartDateTime] FROM [Telemarketing].[dbo].PDCampaignBatch GROUP BY [StartDateTime])
but the problem is it does not display this row
even if that data does not exist in my main table. What seems to be the problem?
NOT IN has strange semantics. If any values in the subquery are NULL, then the query returns no rows at all. For this reason, I strongly recommend using NOT EXISTS instead:
SELECT t.*
FROM [Telemarketing].[dbo].[PDCampaignBatch_temp] t
WHERE NOT EXISTS (SELECT 1
FROM [Telemarketing].[dbo].PDCampaignBatch cb
WHERE t.StartDateTime = cb.StartDateTime
);
If the set is evaluated by the SQL NOT IN condition contains any values that are null, then the outer query here will return an empty set, even if there are many [StartDateTime]s that match [StartDateTime]s in the PDCampaignBatch table.
To avoid such issue,
SELECT *
FROM [Telemarketing].[dbo].[PDCampaignBatch_temp]
WHERE [StartDateTime] NOT IN (
SELECT DISTINCT [StartDateTime]
FROM [Telemarketing].[dbo].PDCampaignBatch
WHERE [StartDateTime] IS NOT NULL
);
Let's say PDCampaignBatch_temp and PDCampaignBatch happen to have the same structure (same columns in the same order) and you're tasked with getting the set of all rows in PDCampaignBatch_temp that aren't in PDCampaignBatch. The most effective way to do that is to make use of the EXCEPT operator, which will deal with NULL in the expected way as well:
SELECT * FROM [Telemarketing].[dbo].[PDCampaignBatch_temp]
EXCEPT
SELECT * FROM [Telemarketing].[dbo].[PDCampaignBatch]
In production code that is not a one-off, don't use SELECT *, write out the column names instead.
Most likely your issue is with the datetime. You may be only displaying a certain degree of percision like the year/month/date. The data may be stored as year/month/date/hour/minute/second/milisecond. If so you have to match down the the most granluar measurement of the data. If one field is a date and the other is a date time they also will likely never match up. Thus you always get no responses.

SQL Variable not getting set

I might be missing something easy here, as someone relatively new to SQL, I am trying the following query:
DECLARE #uidd VARCHAR
SELECT #uidd = UID
FROM PRODUCTOBJECT WHERE EMAIL='abc#gmail.com';
SELECT ProductID FROM PRODUCTUIDMAPPING WHERE UID= #uidd;
And it is returning zero rows. What's puzzling is that if do not use variables and run both selects separately, I do get the right row back.
SELECT UID FROM TABLEA WHERE EMAIL='abc#gmail.com';
returns the UID, and then
SELECT ProductID FROM TABLEB WHERE UID='123456';
will return the ProductID value, where '123456' would be the value returned from the first query.
The problem is when combined together, using the variable for some reason it seems #uidd is not being set. Any reason why ?
I am running this on SQL Server 2008.
Presumably, uuid is longer than one character. Never use character types in SQL Server without a length. The default varies by context and may not be correct.
So, this should work:
DECLARE #uidd VARCHAR(255);
SELECT #uidd = UID
FROM PRODUCTOBJECT
WHERE EMAIL='abc#gmail.com';
SELECT ProductID
FROM PRODUCTUIDMAPPING
WHERE UID= #uidd;