How to query multiple conditions with multiple columns, like AND clause in AND clause.
Example data
| Name | Country | ZipCode |
| -------- | -------------- | -------------- |
| A | Italy | 2020 |
| B | Japan | 1010 |
| C | Canada | 3030 |
| D | Japan | 1011 |
| E | Japan | 1012 |
The result that I need is without..
Country = Japan and ZipCode = 1010
Country = Canada and ZipCode = 3030
| Name | Country | ZipCode |
| -------- | -------------- | -------------- |
| A | Italy | 2020 |
| D | Japan | 1011 |
| E | Japan | 1012 |
I try to write SQL like :
SELECT * FROM table
WHERE ((Country <> 'Japan' AND ZipCode <> '1010') AND
(Country <> 'Canada' AND ZipCode <> '3030'))
but it's not correct. Any help please ?
Just use not:
where not (country = 'Japan' and ZipCode = '1010' or
Country = 'Canada' and ZipCode = '3030'
)
You can expand this out of course:
where (country <> 'Japan' or ZipCode <> '1010') and
(country <> 'Canada' or Zipcode <> '3030')
Note: This constructs will also filter out NULL values. There are none in your sample data and they can both be tweaked to handle them if necessary.
Related
I was trying to learn non-equi joins when I encountered this problem. I have a table pops:
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| country | varchar(100) | YES | | NULL | |
| continent | varchar(100) | YES | | NULL | |
| population | bigint(20) | YES | | NULL | |
I was trying to find countries with their population in the vicinity of say, 100.
select distinct
p1.country,
p2.country,
p1.population,
p2.population
from pops p1
inner join pops p2
on p1.population between p2.population - 100 and p2.population + 100
and p1.country <> p2.country
where p2.country <> p1.country
output I got was:
+------------+------------+------------+------------+
| country | country | population | population |
+------------+------------+------------+------------+
| pakistan | india | 99988 | 99999 |
| china | india | 99990 | 99999 |
| bangladesh | japan | 999 | 999 |
| india | pakistan | 99999 | 99988 |
| china | pakistan | 99990 | 99988 |
| japan | bangladesh | 999 | 999 |
| india | china | 99999 | 99990 |
| pakistan | china | 99988 | 99990 |
+------------+------------+------------+------------+
as we can see, I am getting pairs of (india, pakistan) as well as (pakistan, india), which is data-wise the same thing. Is it possible to eliminate one of the records from the pair?
You could decide to always have the lexographically first (or last, for argument's sake) country on the p1 side - use < (or >) instead of <>.
Also, note that your where clause is redundant, since you already have this condition in the on clause of the join:
select p1.country,
p2.country,
p1.population,
p2.population
from pops p1
inner join pops p2
on p1.population between p2.population - 100 and p2.population + 100 and
p1.country < p2.country
-- Here ------------------^
just change the join condition of p1.country <> p2.country to p1.country < p2.country
I have this locations table:
+----+-----------+------------+----------+
| id | country | state | city |
+----+-----------+------------+----------+
| 1 | US | Georgia | Atlanta |
| 2 | US | California | |
| 3 | US | | |
| 4 | Canada | Ontario | |
| 5 | Canada | Manitoba | Winnipeg |
| 6 | Canada | | |
I want to create a query but could not build my ORDER BY properly. This is the result that I want:
+----+-----------+------------+----------+
| id | country | state | city |
+----+-----------+------------+----------+
| 6 | Canada | | |
| 3 | US | | |
| 4 | Canada | Ontario | |
| 2 | US | California | |
| 5 | Canada | Manitoba | Winnipeg |
| 1 | US | Georgia | Atlanta |
Basically, this is the priority that I want to follow:
Country listing. Alphabetical order.
State listing. Alphabetical order.
City listing. Alphabetical order.
This query does not seem to account for empty columns (I am not using NULLs in my locations table):
SELECT * FROM locations
ORDER BY
country,state,city
Try this ORDER BY clause:
SELECT *
FROM locations
ORDER BY
CASE WHEN state = '' AND city = '' THEN 0
WHEN city = '' THEN 1
ELSE 2 END,
country,
state,
city;
This sorting logic places first those records which are missing both state and city. Next follows records missing city only, followed last by records having non empty data for all three fields.
For versions pre 8.0...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,country VARCHAR(20) NOT NULL
,state VARCHAR(20) NULL
,city VARCHAR(20) NULL
);
INSERT INTO my_table VALUES
(1,'US','Georgia','Atlanta'),
(2,'US','California',NULL),
(3,'US',NULL,NULL),
(4,'Canada','Ontario',NULL),
(5,'Canada','Manitoba','Winnipeg'),
(6,'Canada',NULL,NULL);
SELECT id
, country
, state
, city
FROM
( SELECT x.*
, CASE WHEN #prev=country THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=country
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY country
, city
, state
) a
ORDER
BY i
, country;
+----+---------+------------+----------+
| id | country | state | city |
+----+---------+------------+----------+
| 6 | Canada | NULL | NULL |
| 3 | US | NULL | NULL |
| 4 | Canada | Ontario | NULL |
| 2 | US | California | NULL |
| 5 | Canada | Manitoba | Winnipeg |
| 1 | US | Georgia | Atlanta |
+----+---------+------------+----------+
select *
from locations
order by
state <> '',
city <> '',
country,
state,
city
db-fiddle
Note that in MySQL a boolean expression returns 0 (for FALSE) or 1 (for TRUE). That means for an empty state string state <> '' will return 0 and thus ordered first.
I have 2 tables with the below structure where there is no relation of (PK / FK).
======== country =======
+----+-------+---------+
| id | name | visible |
+----+-------+---------+
| 1 | kkk | 0 |
| 2 | mmm | 1 |
| 3 | ttt | 1 |
| 4 | kkkkk | 0 |
+----+-------+---------+
============ city =============
+----+------+---------+-------+
| id | name | visible | c_id |
+----+------+---------+-------+
| 3 | k333 | 0 | 1 |
| 2 | k222 | 1 | 1 |
| 1 | kkk | 1 | 1 |
| 4 | k444 | 0 | 2 |
| 6 | k666 | 0 | 2 |
+----+------+---------+-------+
I am using country.id and city.country_id as the link between the 2 tables. I am trying to delete the countries and cities where visibility value is 0. After searching I came up with this piece of code:
delete country , city from country, city where city.country_id = country.id and country.id in (select id from country where visible = 0);
But it returns the below error:
ERROR 1093 (HY000): You can't specify target table 'country' for update in FROM clause
I tried to use JOIN with WHERE like this :
DELETE country , city
FROM country JOIN city
ON city.country_id = country.id
WHERE country.visible = 0
It worked well, but there is one more row which is having the value of 0 was not deleted.
======== Country ======
+----+-------+---------+
| id | name | visible |
+----+-------+---------+
| 2 | mmm | 1 |
| 3 | ttt | 1 |
| 4 | kkkkk | 0 |
+----+-------+---------+
#Mihai After I checked your code again I just added WHERE and it worked like this:
DELETE country, city
FROM country
LEFT JOIN city
ON city.country_id = country.id
WHERE country.visible = 0;
Use a JOIN with a WHERE, it is much clearer.
DELETE country , city
FROM country JOIN city
ON city.country_id = country.id
WHERE country.visible = 0
ON your sample data the kkk row with visible=0 wont be deleted since it has an id of 4 which doenst exist in the city table so it won`t be picked up in the join.
Task: From MySQL standard database "world" select all the cities with population more then a million, limiting number of countries to 10.
If I do this:
SELECT country.Name, city.Name, city.Population
FROM country
INNER JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000
LIMIT 10;
it produces this:
+-------------+--------------+------------+
| Name | Name | Population |
+-------------+--------------+------------+
| Afghanistan | Kabul | 1780000 |
| Algeria | Alger | 2168000 |
| Angola | Luanda | 2022000 |
| Argentina | Buenos Aires | 2982146 |
| Argentina | La Matanza | 1266461 |
| Argentina | Córdoba | 1157507 |
| Armenia | Yerevan | 1248700 |
| Australia | Sydney | 3276207 |
| Australia | Melbourne | 2865329 |
| Australia | Brisbane | 1291117 |
+-------------+--------------+------------+
Argentina, for example, is repeated 3 times. I need to limit number of countries to 10, not number of rows.
GROUP BY won't do since I need all the cities, I don't need them grouped.
The expected result would be this:
+--------------------+----------------------------+------------+
| Name | Name | Population |
+--------------------+----------------------------+------------+
| Afghanistan | Kabul | 1780000 |
| Algeria | Alger | 2168000 |
| Angola | Luanda | 2022000 |
| Argentina | Buenos Aires | 2982146 |
| Argentina | La Matanza | 1266461 |
| Argentina | Córdoba | 1157507 |
| Armenia | Yerevan | 1248700 |
| Australia | Sydney | 3276207 |
| Australia | Melbourne | 2865329 |
| Australia | Brisbane | 1291117 |
| Australia | Perth | 1096829 |
| Azerbaijan | Baku | 1787800 |
| Bangladesh | Dhaka | 3612850 |
| Bangladesh | Chittagong | 1392860 |
| Brazil | São Paulo | 9968485 |
| Brazil | Rio de Janeiro | 5598953 |
| Brazil | Salvador | 2302832 |
| Brazil | Belo Horizonte | 2139125 |
| Brazil | Fortaleza | 2097757 |
| Brazil | BrasÃlia | 1969868 |
| Brazil | Curitiba | 1584232 |
| Brazil | Recife | 1378087 |
| Brazil | Porto Alegre | 1314032 |
| Brazil | Manaus | 1255049 |
| Brazil | Belém | 1186926 |
| Brazil | Guarulhos | 1095874 |
| Brazil | Goiânia | 1056330 |
| United Kingdom | London | 7285000 |
| United Kingdom | Birmingham | 1013000 |
+--------------------+----------------------------+------------+
As you can see, number of countries is 10, no matter how many cities.
Of course, my database is different. I used the "world" for simplicity, generality, and availability.
In the real db I also do some filtering on "country".
I Hope this will work
SELECT country.Name, city.Name, city.Population
FROM country
LEFT JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000
AND city.id IN (SELECT id FROM city WHERE city.Population >= 1000000 AND country.Code = city.CountryCode LIMIT 10);
//So sorry try this query
SELECT country.Name, city.Name, city.Population
FROM country
LEFT JOIN city
ON country.Code = city.CountryCode
WHERE city.Population >= 1000000 and country.Code IN(SELECT distinct CountryCode FROM city WHERE Population >= 1000000 LIMIT 10);
Generate temporary row level id's for each of the unique country in a sub query and select rows from it applying a where condition like where temp_row_num is <= 10.
Example:
select
/*uq_country_row_num, */
country_name, city_name, population
from (
select country_name, city_name, population
, case when #pn=(#cn:=country_name)
then #r:=(#r + 1)
else #r:=1
end as uq_country_row_num
, #pn:=#cn as temp_cn
from population,
(select #pn:='', #cn:='', #r:=0) country_rown_nums
-- use order by country_name if the result set is un-ordered
order by
country_name
) all_results
where
uq_country_row_num <= 10
;
You can un comment the variable /*uq_country_row_num, */, if you want them as part of the output.
Example # SQL Fiddle
Edit:
From the comments:
You mean 10 rows per country or all records from max 10 countries?
Exactly, "all records from max 10 countries".
select
country_id,
country_name, city_name, population
from (
select
country_name, city_name, population
, case when #pn=(#cn:=country_name)
then #country_id:=#country_id
else #country_id:=(#country_id + 1)
end as country_id
, #pn:=#cn as temp_cn
from population,
(select #pn:='', #cn:='', #row_num:=0, #country_id:=0) country_row_nums
order by country_name
) all_results
where
country_id <= 10
;
Example 2 # SQL Fiddle
Try this once
SELECT city.Name, city.Population, newcountry.name
FROM city
INNER JOIN
(select distinct Name,Code
from country limit 10) newcountry
ON newcountry.Code = city.CountryCode
WHERE city.Population >= 1000000;
None of the answers worked. So I had to use the following request:
SELECT co.Code, co.Name, ci.Name, ci.Population
FROM city ci
INNER JOIN (
SELECT DISTINCT co.Code, co.Name
FROM country co
INNER JOIN city ci
ON ci.countryCode = co.Code
WHERE ci.Population > 1000000
ORDER BY co.Name
LIMIT 10
) co
ON ci.countryCode = co.Code
WHERE ci.Population > 1000000;
It seems rather clumsy and not efficient to me (notice the repetition of the condition), but for lack of a better way, this is the only solution.
Thanks to everyone for your answers anyway.
Lets say I have the following tables:
Countries
---------------------------
| ID | Country Name |
---------------------------
| 1 | Greece |
| 2 | Italy |
| 3 | Spain |
---------------------------
Cities
---------------------------
| ID | City |
---------------------------
| 1 | Athens |
| 2 | Patra |
| 3 | Rome |
| 4 | Venice |
---------------------------
Countries & Cities
--------------------
| ID | Cntr | City |
--------------------
| 1 | 1 | 2 |
| 2 | 1 | 1 |
| 3 | 2 | 3 |
--------------------
Now, How can I run a MySQL query that will return the name of the countries and the total cities based on table "Countries & Cities" ?
In example to return:
---------------------------
| Cities | Country |
---------------------------
| 2 | Greece |
| 1 | Italy |
---------------------------
Try this out:
SELECT COUNT(cs.City) as Cities, cn.name as Country
FROM countries cn
INNER JOIN country_city cs ON cs.Cntr = cn.id
GROUP BY cn.name
OUTPUT:
2 | Greece
1 | Italy
There is only one JOIN is needed
SELECT `c`.`name`, COUNT(`c`.id)
FROM `countries_cities` AS `cc`
JOIN `countries` AS `c`
ON `c`.id = `cc`.country_id
GROUP BY `cc`.country_id
SELECT cn.Name, COUNT(*)
FROM CountriesAndCities cc
JOIN Countries cn ON (cn.ID = cc.Cntr)
GROUP BY cn.Name
You only need to group the countries in the countries & cities table:
SELECT COUNT(1), c.Name
FROM [countriesAndCities] cnc
INNER JOIN [country] c ON cnc.cnt = c.id
GROUP BY c.Name