Alternative to multiple AND operator - sql-server

is there a cleaner(better) alternative to using multiple AND operator in T-sql.
for example
use Portal
go
SELECT
U.Id,
U.FirstName,
U.LastName,
U.Email,
U.Gender,
U.Country
FROM AspNetUsers U
WHERE (U.M01 >= 80 and U.M02 >= 80 and u.M03 >= 80 AND U.M04 >= 80)

If AND is what you need then AND is the proper operator.
AND is efficient as it can give up as soon as it gets a single false.
The query optimizer is (typically) smart enough to look for the easy false first.
OR is not as efficient so some times you will refactor to get rid of the OR but I have never had a reason to refactor an AND.
If the value changes then maybe go with this for convenience but it should not effect execution
declare #count int = 80;
SELECT
U.Id,
U.FirstName,
U.LastName,
U.Email,
U.Gender,
U.Country
FROM AspNetUsers U
WHERE U.M01 >= #count
and U.M02 >= #count
and u.M03 >= #count
AND U.M04 >= #count;

No, if result depends on columns that you have in WHERE. If there will be good index it will be fast too (good index for this query is
create index [some name] on AspNetUsers (M01, M02, M03, M04) include (Id, FirstName, LastName, Email, Gender, Country)
But this query looks strange and probably it is problem with design/normalization

There are few options to simplify this particular selection syntax but it is hard to say if they make sense without knowing your entire context and your definition of better/cleaner...
Let's assume that cleaner/better means "selection without conjunction operators" for you.
Some of possible options:
Create a view that will prefilter your AspNetUsers table. Then your query syntax could be simplified to:
SELECT
Id,
FirstName,
LastName,
Email,
Gender,
Country
FROM AspNetUsersPerfiltered
Move filtering logic to dedicated function that will check the condition. Then your query syntax could be simplified to:
SELECT
Id,
FirstName,
LastName,
Email,
Gender,
Country
FROM AspNetUsers
WHERE myFunction(M01, M02, M03, M04) = 1
Create additional column(flag) that will be set during row insert/update or by some background process. Then your query syntax could be simplified to:
SELECT
Id,
FirstName,
LastName,
Email,
Gender,
Country
FROM AspNetUsers
WHERE FlagColumn = 1
HTH

Related

How much compound index is slower than single index

I'm thinking about removing index (A) and create compound index (A,B), because A and B are often use together in where clause.
However I wonder how will it impact query if only A will be requested in where clause.
Do you know how could it behave on different databases?
(index is created using default type on for example 10million record tables)
The best way to find out is to actually do it :)
So I created a table:
CREATE TABLE TestTable (ID INT,
FirstName VARCHAR(50),
LastName VARCHAR(50),
City VARCHAR(50))
Then inserted 100 xxx rows:
INSERT INTO TestTable (ID,FirstName,LastName,City)
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY a.name) RowID,
'Bob1',
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%2 = 1 THEN 'Smith2'
ELSE 'Brown3' END,
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 1 THEN 'New York4'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 5 THEN 'San Marino5'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 3 THEN 'Los Angeles6'
ELSE 'Houston7' END
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
then I've created two separate indexes:
CREATE INDEX IX_FirstName ON TestTable(FirstName)
GO
CREATE INDEX IX_LastName ON TestTable(LastName)
GO
Query:
SELECT FirstName,
LastName,
COUNT(*) RowsCount
FROM dbo.TestTable
GROUP BY FirstName, LastName, City
Execution plan with two separate indexes:
One composite index:
CREATE INDEX IX_FirstName_LastName
ON TestTable(FirstName, LastName)
GO
Query:
SELECT FirstName,
LastName,
COUNT(*) RowsCount
FROM dbo.TestTable
GROUP BY FirstName, LastName, City
Execution plan with one composite index:
Conclusion:
It really depends on many factors such as database management system, version of database management system. However, it can be seen that there is no difference between two separate indexes and one compound index.
Pay attention, if you would like to use compound index then you should use all columns of a compound index in your query.

Same query giving different results

I am still new to working in databases, so please have patience with me. I have read through a number of similar questions, but none of them seem to be talking about the same issue I am facing.
Just a bit of info on what I am doing, I have a table filled with contact information, and some of the contacts are duplicated, but most of the duplicated rows have a truncated phone number, which makes that data useless.
I wrote the following query to search for the duplicates:
WITH CTE (CID, Firstname, lastname, phone, email, length, dupcnt) AS
(
SELECT
CID, Firstname, lastname, phone, email, LEN(phone) AS length,
ROW_NUMBER() OVER (PARTITION BY Firstname, lastname, email
ORDER BY Firstname) AS dupcnt
FROM
[data.com_raw]
)
SELECT *
FROM CTE
WHERE dupcnt > 1
AND length <= 10
I assumed that this query would find all records that have duplicates based on the three columns that I have specified, and select any that have the dupcnt greater than 1, and a phone column with a length less than or equal to 10. But when I run the query more than once I get different result sets each execution. There must be some logic that I am missing here, but I am completely baffled by this. All of the columns are of varchar datatype, except for CID, which is int.
Instead of ROW_NUMBER() use COUNT(*), and remove the ORDER BY since that's not necessary with COUNT(*).
The way you have it now, you are chunking up records into similar groups/partitions of records by firstname/lastname/email. Then you are ORDERING each group/partition by firstname. Firstname is part of the partition, meaning every firstname in that group/partition is identical. You will get different results depending on how SQL Server fetches the results from storage (which record it found first is 1, what it found second is 2). Every time it fetches records (every time you run this sql) it may fetch each record from disk or cache at a different order.
Count(*) will return ALL duplicate rows
So instead:
COUNT(*) OVER (PARTITION BY Firstname, lastname, email ) AS dupcnt
Which will return the number of records that share the same firstname, lastname, and email. You then keep any record that is greater than 1.
ORDER BY Firstname is non-deterministic here as they all have the same Firstname from the partition by
If CID is unique you could use that for the order by but I suspect you really want count.
I believe you are getting different results with every run would be because (a) unless clearly specified in the query, you can assume nothing about the order in which SQL return data in a query, and (b) the only ordering criteria you provide is by FirstName, which is far less precise than your grouping (Firstname, lastname, email).
As for the query itself, as written it assumes that the first item found in a given partition contains a valid phone number. Without specifying the order, you cannot know this will be true… and what if all items in a given grouping have invalid phone numbers? Below is my stab at pulling out the data you're looking for, in a hopefully useful format.
WITH CTE -- Sorry, I'm lazy and generally don't list the columns
AS
(
SELECT
Firstname
,lastname
,phone
,count(*) HowMany -- How many in group
,sum(case len(phone) when 10 then 1 else 0 end) BadLength -- How many "bad" in group
from data.com_raw
group by
Firstname
,lastname
,phone
having count(*) <> sum(case len(phone) when 10 then 1 else 0 end)
and count(*) > 1 -- Remove this to find singletons with invalid phone numbers
)
select
cr.CID
,cr.Firstname
,cr.lastname
,case len(cr.phone) when 10 then '' else 'Bad' end) IsBad
,cr.phone
,cr.email
from data.com_raw cr
inner join CTE
on CTE.Firstname = cr.Firstname
and CTE.lastname = cr.lastname
and CTE.phone = cr.phone
order by
cr.CID
,cr.Firstname
,cr.lastname
,case len(cr.phone) when 10 then '' else 'Bad' end)
,cr.phone
(Yes, if there are no indexes to support this, you will end up with a table scan.)
SELECT Firstname, lastname,email, COUNT(*)
FROM [data.com_raw]
GROUP BY Firstname, lastname,email HAVING COUNT(*)>1
WHERE LEN(PHONE)<= 10

MS SQL variable add value from count in the same statement

I have this Select statement
select Id, UserName, from UserTable where Department= #DepartmentInput
and a variable to calculate how many users from the department
#UserCounter
Is there a way to add count(the number of users from the department) into #UserCounter from the select statement should the statement runs in a while loop?
Presuming sql-server:
select Id, UserName, #UserCounter = Count(*) OVER (Partition By Department)
from UserTable
where Department= #DepartmentInput
Otherwise a simple sub-query should work too:
select Id, UserName, #UserCounter = (select count(*) from UserTable
where Department= #DepartmentInput)
from UserTable
where Department= #DepartmentInput
The database should be clever enough to optimize that query so that the subquery doesn't need to be avaulated for every (matching) row.

ORDER BY select statement using UNION & DISTINCT

I'm using the following query to populate a dropdown list of values.
select 'Select a City' as City, 'All' as Value
UNION ALL
select distinct City, City as Value from BND_Listing
I'd like to sort A-Z the results. I've tried the following:
select 'Select a City' as City, 'All' as Value
UNION ALL
select distinct City, City as Value from BND_Listing
ORDER BY City ASC
But am getting an error:
Incorrect syntax near the keyword 'Union'.
Additionally this query is pulling "Blank or NULL" values and displaying a blank space at the top of the drop-down. I'd like to hide that if possible. Not display any null value?
You want to add a row to your result, which is always on top and carries a NULL as ID?
Try this:
SELECT *
FROM
(
SELECT NULL AS col1,'select an object' AS col2,0 AS SortInx
UNION ALL
SELECT TOP 10 object_id,name,ROW_NUMBER() OVER(ORDER BY name)
FROM sys.objects
) AS Sortable
ORDER BY SortInx
Short explanation: ROW_NUMBER() start with 1, so the first row gets 0 as sort index. The numbers from 1 to x represent the sorted name's order.
The outer SELECT will sort the result-set again making sure, that 0 is in front and 1 to x following...
I agree with most of the comments here where the best approach is to actually have the "Select a Value" row added in the application itself. It's probably best to have the database only delivering "actual" data to your application and handle things like that in the code.
I'm also not sure what this project is for, but if you have access, I would strongly recommend creating views and/or stored procedures at the database level to abstract any database schema and logic changes from your application.
Just out of curiosity, why are you selecting the same field twice with different aliases? I'm assuming you're setting the display value and the actual value in a simple HTML dropdown, but in this case, the values are the same, so you could only have one field in your result set and reference that value twice in the application. Doing this would solve the problem of your original question, as well as simplify your query (although a query this simple is going to have negligible cost anyway) to look like this:
SELECT DISTINCT City
FROM BND_Listing (NOLOCK)
WHERE City IS NOT NULL
ORDER BY 1 ASC
Depending on the data, DB config, etc, you may need to account for empty strings and/or leading/trailing spaces with something like this:
SELECT DISTINCT LTRIM(RTRIM(City)) AS City
FROM BND_Listing (NOLOCK)
WHERE LTRIM(RTRIM(City)) <> ''
AND City IS NOT NULL
ORDER BY 1
Sorry...I know that may have been a little overkill, but you said you were new to SQL, so I thought I'd just share that in case your NULL results were actually empty strings.
Thank you everyone for the responses it gave me a lot of insight on where to look for my problem. The original query with the addition of the below achieved the proper result.
working query:
select 'Select a City' as City, 'All' as Value
UNION ALL
select distinct City, City as Value from BND_Listing
where isnull(City,'') <> ''
Order by City ASC
with 'Select a City' always at the top of the dropdown. Credit to #scsimon on my other post for this.
with cte as(
select 'Select a City' as City, 'All' as Value
UNION ALL
select distinct City, City as Value from BND_Listing
where isnull(City,'') <> '')
select * from cte Order by case when City = 'Select a City' then 1 else 2 end, City ASC

advantage of select query in from clause

What is the advantage of using select clause in the from clause over normal select clause ?. For ex.
select name, age, address from emp where
empid = 12.
what is the advantage of below query over above query.
select A.name, A.age, A.address from (select
* from emp where empid = 12) as A.
The inner query creates a temp view and from that result, the fields in the first query selected. Right ?. But the query mentioned in the top of this question can also be used to get the same result.
What is the advantage? Thanks.
One way this technique can be used to derive results in the inner query that you don't want presented in the outer query. A simple example, I want to see the oldest person in each household, here is one way to do it:
SELECT name, age, address FROM
(
SELECT name, age, address,
rn = ROW_NUMBER() OVER (PARTITION BY address ORDER BY age DESC)
FROM dbo.emp
) AS x
WHERE rn = 1;
This way the derived column (a ranking, essentially, of household members by age, oldest first) does not need to be a column in the result set (and this also makes it convenient for filtering).
And I'm sure there are plenty of others (such as not having to repeat elaborate expressions); this was just the first one that came to mind.
In the case you posted in your question, I don't see an advantage at all. If you post a real example, where someone said there was an advantage, we might be able to explain why (or at least why they may have thought that).

Resources