Exclude multiple values using <> - sql-server

I am trying to create a query that returns results excluding 4 specific values from a columns.
Here is my code:
SELECT CustomerID,
ContactName,
Country
FROM Customers
WHERE CustomerID IN (SELECT CustomerID
FROM Customers
WHERE (Country <> ('UK')('Australia')('Canada')));
This works if I exclude only one country but not multiple.
Thanks in advance for any help you can provide.

Use NOT IN operator
The list should be separated by comma no need to have open/close parenthesis for each value inside the list. Also you don't need sub-query just add the filter Where clause
SELECT CustomerID,
ContactName,
Country
FROM Customers
WHERE Country NOT IN ( 'USA', 'UK', 'Australia', 'Canada' )
Make sure you dont pass any NULL values inside the list because NOT IN fails when there is a NULL value present in the list

Select
CustomerID, ContactName, Country
From Customers
Where CustomerID NOT IN ('USA','UK','Australia','Canada');

You may consider defining excluded countries in a special table (e.g. ExcludedCountry). This will separate your data (excluded countries) from your logic (select customer not in excluded countries). In this case, your query can look like:
select CustomerID, ContactName, Country
from Customers C
left join ExcludedCountry EC ON EC.Name = C.CustomerID
where CustomerID IS NOT NULL
or
-- may be faster than above
select CustomerID, ContactName, Country
from Customers C
where not exists (
select 1
from ExcludedCountry EC
where EC.Name = C.CustomerID)
The advantage of this solution is an easier management of your excluded items.

Related

detecting duplicates and removing them

I've been trying to solve a problem in my database which is quite common but I couldn't find a solution so far and I hope you could help me with this.
I have a database with people and their associated addresses. My primary goal is to find out how many unique households are in there. For example, I want to count a family as one. So far a ran a query to display last_names and addresses which are more than one:
select Last_Name ,add_line1, count(*) from ##all_people
group by Last_Name,ADD_LINE1
having count(*) > 1
This shows me people with the same last_name and address but I need their IDs in order to remove them from my temptable.
Furthermore, I'd like to ask how it is possible to display only one record for each household.
This is the structure of my temptable:
ID First_name Last_Name add_line1
Thank you so much for your help!!!
to find duplicates, you can use Count() Over() and partition by the grouping you want.
select * from (
select Id, Last_Name ,add_line1, count(*) over (partition by Last_Name, add_line1) dupe_count from ##all_people
) t
where t.dupe_count > 1
to find the ones you want to delete, you can use Row_Number()
select * from (
select Id, Last_Name ,add_line1, row_number() over (partition by Last_Name, add_line1 order by ID) extras from ##all_people
) t
where t.extras > 1
use t.extras = 1 to see one row per grouping
You seem to have a lot of questions here...
My primary goal is to find out how many unique households are in there.
You can do this with a distinct count:
SELECT COUNT(DISTINCT Last_Name + add_line1)
FROM ##all_people
...but I need their IDs in order to remove them from my temptable
I think this is solved by the new count query.
Furthermore, I'd like to ask how it is possible to display only one record for each household.
Just use distinct last name and address:
SELECT DISTINCT last_name, add_line1
FROM ##all_people

Repeat Customers with multiple purchases on the same day counts a 1

I am trying to wrap my head around this problem. I was asked to create a report that show repeat customers in our database.
One of the requirements is if a customer has more than 1 order on a specific date, it would only count as 1.
Then if they have more than 1 purchase date, they would then count as a repeat customer.
Searching on here, I found this which works for finding the Customers with more then 1 purchase on a specific purchase date.
SELECT DISTINCT s.[CustomerName], s.PurchaseDate
FROM Reports.vw_Repeat s WHERE s.PurchaseDate <> ''
GROUP BY s.[CustomerName] , cast(s.PurchaseDate as date)
HAVING COUNT(*) > 1;
This MSSQL code works like it should, by showing customers who had more than 1 purchase on the same date.
My problem is what would the best approach be to Join this into another query (this is where i need help) that then shows a complete repeat customer list where customers with more than 1 purchase would be returned.
I am using MSSQL. Any help would be greatly appreciated.
You're close, you need to move distinct into your having clause because you want to include only customers that have more than 1 distinct purchase date.
Also, only group by the customer id because the different dates have to be part of the same group for count distinct to work.
SELECT s.[CustomerName], COUNT(distinct cast(s.PurchaseDate as date))
FROM Reports.vw_Repeat s WHERE s.PurchaseDate <> ''
GROUP BY s.[CustomerName]
HAVING COUNT(distinct cast(s.PurchaseDate as date)) > 1;
If you want to pass a parameter to a query and join the result, that's what table-valued functions are for. When you join it, you use CROSS APPLY or OUTER APPLY instead of an INNER JOIN or a LEFT JOIN.
Also, I think this goes without saying, but when you check if PurchaseDate is empty:
WHERE s.PurchaseDate <> ''
Could be issues there... it implies it's a varchar field instead of a datetime (yes?) and doesn't handle null values. You might, at least, want to replace that with ISNULL(s.PurchaseDate, '') <> ''. If it's actually a datetime, use IS NOT NULL instead of <> ''.
(Edited to add sample data and DDL statements. I recommend adding these to SQL posts to assist answerers. Also, I made purchasedate a varchar instead of a datetime because of the string comparison in the query.)
https://technet.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
CREATE TABLE company (company_name VARCHAR(25))
INSERT INTO company VALUES ('Company1'), ('Company2')
CREATE TABLE vw_repeat (customername VARCHAR(25), purchasedate VARCHAR(25), company VARCHAR(25))
INSERT INTO vw_repeat VALUES ('Cust1', '11/16/2017', 'Company1')
INSERT INTO vw_repeat VALUES ('Cust1', '11/16/2017', 'Company1')
INSERT INTO vw_repeat VALUES ('Cust2', '11/16/2017', 'Company2')
CREATE FUNCTION [dbo].tf_customers
(
#company varchar(25)
)
RETURNS TABLE AS RETURN
(
SELECT s.[CustomerName], cast(s.PurchaseDate as date) PurchaseDate
FROM vw_Repeat s
WHERE s.PurchaseDate <> '' AND s.Company = #company
GROUP BY s.[CustomerName] , cast(s.PurchaseDate as date)
HAVING COUNT(*) > 1
)
GO
SELECT *
FROM company c
CROSS APPLY tf_customers(c.company_name)
First thanks to everyone for the help.
#MaxSzczurek suggested I use table-valued functions. After looking into this more, I ended up using just a temporary table first to get the DISTINCT purchase dates for each Customer. I then loaded that into another temp table RIGHT JOINED to the main table. This gave me the result I was looking for. Its a little(lot) ugly, but it works.

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

Select all Item From Table Where Order has any Items between dates

I have an Orders table and an OrderItem table. I would like to select all OrderItems that have been shipped between 2 dates, and select the additional OrderItem of a certain type that was shipped outside of the 2 dates if it's part of an Order that has OrderItems shipped between the 2 dates.
This seemed really easy when I first thought of it, but I'm having a hard time putting it into a SQL statement. I'm using SQL Server.
EDIT:
Yes, I am familiar with the between keyword. What I have is an Order, Say Order #10001. It has 2 items, a product that is shipped on 01/20/2015 and a warranty that is marked as shipped on 02/04/2015. So when I run my query:
SELECT *
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
I only get the 1 product, I want to get the warranty that is on the Order as well.
Hope that clarifies my question.
You can do this like this:
SELECT *
FROM OrderItems
WHERE OrderID IN(
SELECT DISTINCT OrderID
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
)
Or:
SELECT *
FROM OrderItems oi1
JOIN (
SELECT DISTINCT OrderID
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
) oi2 ON oi1.OrderID = oi2.OrderID
Are you familiar with BETWEEN keyword?
SELECT ...
WHERE col BETWEEN AND
If you add more information, such as sample data to your question, I can elaborate on the answer.

How to put a subquery within an aggregate subquery

I am using SQL-Server 2000 and am trying to find duplicates with certain conditions. Someone here helped me earlier with the duplicate part which was great, however, I can't figure out how to filter the duplicated cases further.
I need to move the "where" statement to the subquery so that I only get contractor duplicated names as opposed to all duplicated names which is what's happening with this code (the code is first finding all duplicates and then filtering out the contractors and I'd like it to do the opposite). The problem is that I'm mixing it into an aggregate statement and it's giving me an error. I tried to put in another subquery within the subquery but it still gave me an error.
Any help is appreciated. Here's a simpler (I'm learning) version of the code:
SELECT DISTINCT(c1.contactid) as 'ContactID', c1.lastname as 'Last Name', c1.firstname as 'First Name'
FROM contacts c1 INNER JOIN (SELECT lastname, firstname FROM contacts group by lastname, firstname
HAVING count(*)>1)
dups on c1.lastname=dups.lastname and c1.firstname=dups.firstname
WHERE (c1.contractor=1)
For the example code you have given, placing the where clause between the "from contacts" and "group by lastname, firstname" should do they trick.
i think this is what you want:
SELECT
contact.ContactId,
contact.FirstName,
contact.LastName
FROM contacts as contact
INNER JOIN
(
SELECT ContactID FROM contacts GROUP BY FirstName, LastName HAVING Count(*) > 1
) AS Dups
ON Dups.ContactId = contact.ContactId
WHERE
contact.isContractor = 1
you are very close. this will provide you a list of each individual record that has a matching record with the same first name and last name. Hope this helps
Select C.contactid As ContactID
, C.LastName As [Last Name]
, C.FirstName As [First Name]
From contacts C
Inner Join (
Select C1.LastName, C1.FirstName
From contacts As C1
Where C1.contractor = 1
Group By C1.LastName, C1.FirstName
Having Count(*) > 1
) As dups
On C.LastName = dups.LastName
And C.FirstName = dups.FirstName
Where C.contractor = 1
You are going to need the filter on contractor = 1 in both the subquery and the outer query. Otherwise, you might return people that happen to have the same name as a contractor and are duplicated.
Also, you do not need the Distinct keyword if ContactId is the primary key of the Contacts table.
Move the where clause to the subquery. It fill first filter out all non-contractors, and then group the remainder.
SELECT DISTINCT
c1.ContactId
,c1.lastname as 'Last Name'
,c1.firstname as 'First Name'
from contacts c1
inner join (select lastname, firstname
from contacts
where c1.contractor = 1
group by lastname, firstname
having count(*) > 1) dups
on c1.lastname = dups.lastname
and c1.firstname = dups.firstname
Oh, and unless there's a really good reason, you really don't want to include embedded spaces in your column aliases ("Last Name", "First Name").

Resources