Can I use HAVING instead of WHERE in SQL queries? - sql-server

I always thought that I could not, but MSDN says otherwise.
When GROUP BY is not used, HAVING behaves like a WHERE clause.
I had checked and got the error:
Msg 8121:
Column '...' is invalid in the HAVING clause because
it is not contained in either an aggregate function or the GROUP BY.
So, what is it? An error in documentation, or a little-known detail?
This may be one of those things that worked in Sybase SQL Server. Now Microsoft rewrote SQL Server closer to the ANSI standard, but forgot to fix documentation.
This is the case?

You can use having without using group by but on aggregate function.
select avg(price) from tbltemp having avg(price) >= 2
In the MSDN link you provided, the example given instructs us to use where instead of having.
You may NOT write something like "select avg(price) from goods HAVING
price >= 1000" but you may write "select avg(price) from goods WHERE price >= > 1000"

Related

Cannot extract year from createdDate in SOQL query

SELECT House__r.Name, House__r.House_Owner__r.person__r.Email__c, (SELECT Name, Total_Balance__c, Total_Expense__c FROM Expenses__r Where Type__c='Yearly' AND AND CALENDAR_YEAR(CreatedDate) = CALENDAR_YEAR(System.today() ) FROM Member__c
Error=> Unknown parsing error.
Kindly suggest what else can I do.
You have two 'AND' in a row. Also, if you are using query editor, you can't use System.today().
(on mobile, formatting will be poor, sorry)
SOQL supports only "field - operator - value_or_function” style for conditions. You try to make it "function - operator - another function", won't work.
For your particular scenario try with WHERE Created date = THIS_YEAR. It's a special literal, see https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_dateformats.htm
More generic way would be to write what you need as a formula field, you could compare to YEAR(TODAY()). (that could be poor performance to filter by formula, often it results in full table scan, you would have to test, maybe add more filters using indexed columns...).
Or write it as WHERE CreatedDate >= :start AND CreatedDate <= :stop, put right bind variables for your range.

SQL Server Query SELECT Error (now trying LIMIT)

I am working on what should be a super simple query for SQL Server 2014. All I want to do is check that our systems can interface with SQL Server after updates, etc. So I need to just verify that it makes the connection correctly and finds a table within the Server.
Attempt 1:
SELECT TOP (1) *
From [X].[dbo].[Y]
WITH (NOLOCK);
But apparently 'top' is not a supported option with SQL Server 2014.
To add some more, here is the exact error I get when trying to run that: Syntax error. The token 'Top' is invalid. Please check the case of your operators (eg 'or' versus 'OR') and check that your functions use brackets after the function name eg Now(), eg Len("abc").
Attempt 2:
SELECT *
From [X].[dbo].[Y]
WITH (NOLOCK)
LIMIT (1);
That one tells me that I need to put data items between [], text between "", and functions as FunctionName(). However...I don't see where I missed any of those.
Can anybody possibly shed some light on why my query isn't going through? Any help would be appreciated.
The first attempt should work just fine:
SELECT TOP (1) *
From [dbo].[Y]
WITH (NOLOCK);
See example
If it doesn't work, you should include the error message.

SQL Server : function precedence and short curcuiting in where clause

Consider this setup:
create table #test (val varchar(10))
insert into #test values ('20100101'), ('1')
Now if I run this query
select *
from #test
where ISDATE(val) = 1
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
it will fail with
Conversion failed when converting date and/or time from character string
which tells me that the where conditions are not short-circuited and both functions are evaluated. OK.
However if I run
select *
from #test
where LEN(val) > 2
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
it doesn't fail, which tells me that where clause is short-circuited in this case.
This
select *
from #test
where ISDATE(val) = 1
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
and LEN(val) > 2
fails again, but if I move length check to before cast, it work. So looks like the functions are evaluated in the order they appear in query.
Can anyone explain why first query fails?
It fails because SQL is declarative so the order of your conditions is not taken into account when the plan is generated (nor is it required to do so).
The usual way to get around this is to use CASE which has strict rules about sequence and when to stop.
In your case you will probably need nested CASEs, something like this:
WHERE
(
case when ISDATE(val) = 1 then
case when CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' and
LEN(val) > 2
THEN 1 ELSE 0 END
ELSE 0
END
) = 1
(note this is unlikely to be actually correct SQL as I just typed it in).
By the way, even if you get it "working" by rearranging the conditions, I'd advise you don't. Accept that SQL simply doesn't work in that way. As the data changes & stats change, SQL is upgraded, workload varies, indexes are added the query plan could change. Any attempts to "get it working" are going to be short-lived at best so go with the CASE which will continue to work once you've got it right (provided you nest CASE statements where required and don't fall into the same precedence trap in the CASE conditions!)
The mystery is answered if you examine the Execution Plan. Both the CAST() and the LEN() are applied as part of the Table Scan step, while the test for IsDate() is a separate Filter test after the Table Scan.
It appears that the SQL Engine's internal optimizations use certain filtering functions as part of the retrieval of the data, and others as separate filters, almost certainly as a form of query optimization to minimize the load from disk into main memory. However, more complex functions, such as IsDate(), which is dependent on system variables such as system date format in some cases (is '01/02/2017' Jan 2nd or Feb 1st?), need to have the data retrieved before the filter is applied.
Although I have no hard information on this, I strongly suspect that any filter more resource intensive than a certain level is delegated to the Filter steps in the query plan, and anything simple/fast enough to be checked as the data is being read in is applied during the Scan/Seek steps. Also, if a filter could be applied on the data in the index, I am certain that it will be tested before any non-index data is tested, solely to minimize disk reads, which are bad performance juju (this may not apply on the Clustered index of the table). In these cases, the short-circuiting might not be straightforward, with an IsDate() test specified on a non-index field being executed after a similar test on an indexed field, no matter where they are in the list of conditions.
That said, it appears to be true that conditions short-circuit when they are executed in the same step of the query plan. If you insert a string like '201612123' into the temp table, then add a check on Len(val) < 9 after the date comparison, it still generates an error, instead of checking both LEN() conditions at the same time in a tiny optimization.
which tells me that where conditions are not short-circuited and both functions are evaluated.
To expand on LoztInSpace's answer, your terminology suggests you are not interpreting SQL correctly, on its own terms.
The various parts of a SELECT statement are not "functions". The entire statement is atomic. You supply the query as unit, and the DBMS responds. There is no "before" and no "after". There is just the query.
Those are the rules. Your job in formulating the query is to supply one that is valid. It's a logical progression: valid question, valid answer, etc. The moment you step out of that frame, you might as well be asking, "why is the sky seven?".
One a small clarification to #LoztInSpace's answer. When he refers to the order of your statements, he's presumably talking about the phrasing of your query, which for purposes of evaluation is inconsequential. Sequential SQL statements are executed sequentially, as presented. That is guaranteed by the SQL standard.

contains clause sql server

Assume that I had the following t-sql:
select * from customer where contains(suburb, '"mount*"').
Now it brings back both: "Mount Hills" and "Blue Mountain". How do I strict it to search only the very beginning of the word which in this case is only "Mount Hills"?
thanks
'contains' is used for exactly that. Use
select * from customer where charindex('mount', suburb)=1
or
select * from customer where suburb like 'mount%'
but that's slower.
Your query works correctly, you asked server "give me all record where ANY word in column 'suburb'" starts with 'mount'.
You need to be more specific what are you trying to accomplish. Match beginning of the entire value stored in column? LIKE is your friend then.

SQL Server Management Studio - using multiple filters in table list?

In Management Studio, you can right click on the tables group to create a filter for the table list. Has anyone figured out a way to include multiple tables in the filter? For example, I'd like all tables with "br_*" and "tbl_*" to show up.
Anyone know how to do this?
No, you can't do this. When we first got Management Studio I've tried every possible combination of everything you could think of: _, %, *, ", ', &&, &, and, or, |, ||, etc...
You might be able to roll your own addon to SMSS that would allow you to do what you are looking for:
The Black Art of Writing a SQL Server Management Studio 2005 Add-In
Extend Functionality in SQL Server 2005 Management Studio with Add-ins
The first one is specifically for searching and displaying all schema objects with a given name so you might be able to expand upon that for what you are looking for.
I'm using SQL Server Management Studio v17.1 and it has a SQL injection bug in it's filter construction, so you can actually escape default
tbl.name like '%xxx%'
and write your own query (with some limitations). For example to filter tables that are ending with "_arch", "_hist", "_purge" I used following filter value
_arch') and RIGHT(tbl.name, 5) != N'purge' and RIGHT(tbl.name, 4) != N'hist' and not(tbl.name like N'bbb
You can use SQL Server Profiler to see the constructed query and adjust it as needed.
Not sure if this same bug is available in previous SQL Management Studio versions or when it will be fixed, but for now I'm happy with the result.
I've used Toad for SQL Server (freeware version) which has very nice filtering options.
At first it looks like it could use a CONTAINS query (e.g. "br_*" OR "tbl_*"), but it doesn't seem to. It seems to only support a value that is then passed into a LIKE clause (e.g. 'app' becomes '%app%').
The "sql injection" method still works (v17.5), but with a twist:
zzzz' or charindex('pattern1', name) > 0 or charindex('pattern2', name) > 0 or name like 'zzzz
(I used the 'zzzz' to bypass the '%')
It doesn´t work if '_' or '%' is used in the patterns (or anywhere on your code), because it will automatically be replaced by '[_]' or '[%]' before evaluation.
As others have said, you cannot do this in SQL Server Management Studio (up and including 2014).
The following query will give you a filtered list of tables, if this is all you need:
SELECT
CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS TABLE_SCHEMA_AND_NAME,
TABLE_SCHEMA,
TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA IN ('X', 'Y', 'Z') -- schemas go here
ORDER BY
TABLE_SCHEMA,
TABLE_NAME;
The SQL injection method still works (somewhat) as of SSMS 2017 v17.8.1, although it puts brackets around the % symbol, so it will interpret those literally.
If you're using the Name->Contains filter, Profiler shows:
... AND dtb.name LIKE N'%MyDatabase1%')
So, in the Name->Contains field: MyDatabase1') OR (dtb.name LIKE 'MyDatabase2 should do it for simple cases.
This is old I know, but it's good to know that it can works if you input just entering the "filter" text. Skip * or % or any other standard search characters, just enter br_ or tbl_ or whatever you want to filter on.
Your in luck, I just conquered that feat, although my success is small because you can filter by schema which would allow you see more than 1 table but you have to type the filter text in each time you want to change it.

Resources