Using "not in" on WHERE with AND? - mysql

I'm trying to get a better understanding of how "not in" works with WHERE in MySQL.
For example:
SELECT * FROM current_mailing_list
WHERE address1 NOT IN
(select address1 from old_mailing_list) AND
city not in (select city from old_mailing_list);
In the above example, the purpose of the query is to list mailing addresses which are new. address1 is a street address such as 1234 N. Main St. The problem happens when 1234 N. Main St occurs in more than one city, which can happen. So I decided to add city to it to make it more unique.
My question is, is this doing what I expect it to do? Meaning, it should find those street addresses (address1) that don't exist in the old_mailing_list AND then make sure they have a different city.
I have done this, just with the address1:
SELECT * FROM current_mailing_list
WHERE address1 NOT IN
(select address1 from old_mailing_list);
and it produced a much larger list (about 10 times the size). So I wanted to add city to this. Or is my logic in the entirely wrong and need another approach?

Your current query will not return new addresses where EITHER their "address1" or "city" appears at all in the old mailing list. I think you want to select cities where they don't both appear together, like so:
SELECT *
FROM current_mailing_list c
WHERE NOT EXISTS
(
SELECT 1
FROM old_mailing_list
WHERE
address1 = c.address1
AND city = c.city
)
Quite literally; select everything from the current mailing list where there is no record in the old mailing list with the same city and address1.

To use NOT IN, you'll want to combine both the city and address in the same clause as MySQL supports this (other RDBMS do not support this method). As others have pointed out, you won't get your desired results using both separately.
Try this if you want to use NOT IN:
SELECT *
FROM current_mailing_list
WHERE (address1,city) NOT IN
(select address1, city from old_mailing_list);
Sample SQL Fiddle Demo
I prefer to use a LEFT JOIN for this personally though and check for NULL:
SELECT c.*
FROM current_mailing_list c
LEFT JOIN old_mailing_list o ON c.address1 = o.address1 AND c.city = o.city
WHERE o.address1 IS NULL

If 1234 N. Main St. is an address for both a city in the old mailing list and a city in the new mailing list, your query will exclude BOTH addresses. Your query is doing exactly what it says it's doing: you will get addresses where both the street address and the city are not in the old mailing list. If either appear for an address in the old mailing list, that address will not be retrieved.

Related

Query using two tables with DISTINCT

I have two tables - clients and - group
I need to get county and zip from clients and group-assigned from group
When I search, I cannot get distinct results, that is, instead of the output showing 100 clients with zipcode 12345 in jones county in main st group.
I need to have each zip and county listed once by group. I have googled and attempted many ways but it is just beyond me.
Can anyone assist in steering me to the correct way
Adding GROUP BY group, city, zip to the end of your query should get you what you need. It will only return unique combinations of the three.
Presumably you have something like:
select g.*, c.county, c.zip
from clients c join groups g on <some join condition>
You want one result per group. So, add a group by clause such as:
group by g.id -- assuming id uniquely identifies each group
This will give an arbitrary value for the other fields, which may be sufficient for what you are doing. (This uses a MySQL features called Hidden Columns.)

MySQL Struggling with join / selection criteria

I am hoping someone will be able to point me in the right direction.
I have a COUNTRY table, containing a primary ID, and country name (e.g. France)
I have a second table PAYMETHODS_COUNTRIES, which contains a primary id, payment method id (e.g. 4) and country id
I am trying to output a list of checkboxes on each payment method, to control which countries (or users in which countries) have access to those local payment methods.
I have the checkbox list working okay, and the processing of the form is also working. The last remaining thing, is to output the list of country checkboxes with selected countries (e.g. this payment method is available in France and Germany) shown as CHECKED.
So what I am trying to do is to output a complete list of all countries
SELECT id, name FROM country ORDER BY id ASC
And then include the PAYMETHODS_COUNTRIES table something like this (not working of course)
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country
WHERE paymethods_countries.paymethod = 10
This example query only shows rows that actually have a record in PAYMETHODS_COUNTRIES, not what I am after.
What I was hoping for, is a complete list of country.id + country.name, with a final field (the ID of the PAYMETHODS_COUNTRIES table) - which should be null where there is no link. That way I should be able to check for null or otherwise, and output CHECKED for those rows.
Hopefully that makes sense - or if anyone knows a better way to achieve what I am doing, your thoughts are appreciated.
While the other answer here will produce the desired result, semantically it is more correct to move the condition from the WHERE clause to the ON clause:
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country
LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country and paymethods_countries.paymethod = 10
This eliminates the need to add the extra OR condition in the WHERE clause.
It's only coming back with rows that have record in PAYMETHODS_COUNTRIES because of your WHERE clause since you say the paymethod must = 10. Add an OR to let it be null too:
SELECT country.id AS countryid,
country.name AS countryname,
paymethods_countries.id AS methodid
FROM country
LEFT JOIN `paymethods_countries`
ON country.id = paymethods_countries.country
WHERE paymethods_countries.paymethod = 10
OR paymethods_countries.paymethod IS NULL

How do I make sure a value is not returned if it is connected to multiple values, one which is undesired?

My question isn't perfectly phrased, but basically here's the question.
I have two columns in a table "works". One column is a list of last names, and the other is a list of cities where the people live.
I don't want my query to return anybody who lives in Chicago.
My current code is:
select lives.last_name
from lives
where lives.city <> "Chicago";
But some people live in both Chicago and somewhere else, say Miami, and they still show up. How do I only return people who don't live in Chicago without using group or count functions (not that far yet)
Thanks
you can use something like this with NOT EXISTS:
select a.last_name
from lives a
where not exists (select last_name
from lives b
where a.last_name = b.last_name
and city = 'chicago')
See SQL Fiddle with Demo
Try using NOT IN
SELECT last_name
FROM lives
WHERE last_name NOT IN
(
SELECT last_Name
FROM lives
WHERE city = 'Chicago'
)
You may group on last name selecting all cities and then filtering records not containign Chicago:
Select Last_name, City
from lives
group by Last_name
having group_concat(city) not Regexp 'Chicago'

MySQL INNER JOIN syntax

Can someone "break down" the syntax here. Please. I need to learn this ASAP.
From my limited experience -
firstname and lastname are columns and list is a table.
count(id)>1 is used to check if there is more than one row with the same...
That's it. I don't know what this does but I need to understand it.
SELECT firstname, lastname, list.address FROM list
INNER JOIN (SELECT address FROM list
GROUP BY address
HAVING count(id) > 1) dup
ON list.address = dup.address
This query will return a list of all names (first and last name), which contain a duplicate address. This part
SELECT address FROM list
GROUP BY address HAVING count(id) > 1
Gets a list of all the addresses that occur more than once in the table, This is then joined back to the table itself, to return all names which have the same address. This should return a list of all the unique address which have more than 1 name associated with them, along with the names that go along with the addresses.

Normalizing MySQL table with records of another table

i have 2 tables. The city tables is not normalized because the country information is in plain text. I have added the id_country to the 'city' table (that column is empty).
I need to check for matches between city>country and country>country and then update the city records that matched with the id_country from the country table. At the end i will be able to delete the 'country' column from the city table.
City table
id_city (1, 2, 3...)
city (Washington, Guayaquil, Bonn...)
country (Germany, Ecuador, USA...)
id_country (currently empty)
Country table
id_country (1, 2, 3...)
code (GE, EC, US...)
country (Germany, Ecuador, USA...)
I have no idea on where to start and if it can be done with a SQL query. My original idea was to search for matches in a php loop but that seems to be a really harder implementation.
You can do this with a JOIN on an UPDATE statement.
UPDATE city c1 INNER JOIN country c2 ON c1.country=c2.country
SET c1.id_country=c2.id_country;
Using an INNER JOIN will make sure that updates only occur for cities that have a matching country value.
Once you've run it, you'll be able to select all those cities that still have a null id_country just in case some of them didn't match. Conversely, once you've determined that all your cities have an id_country, you can delete that column from the city table.
The city tables is not normalized because the country information is
in plain text.
Nonsense. Normalization doesn't mean "replace plain text with id numbers". Find whoever taught you that and poke him in the eye with a sharp stick.
Your real problem is that "city" plus "country" isn't sufficient to identify cities, at least in the USA. I think there are at least a dozen different cities named "Washington" in the USA.
Instead of replacing the country name with an id number, you'd be far better off replacing it with the two-letter country code. The codes are human-readable; the id numbers will require an additional JOIN in every query that uses your table of cities.
Something like this should work:
UPDATE city set id_country = (SELECT country.id_country from country WHERE country.country = city.country)