I have a table with users and countries either UK or US, now a user can appear multiple times with a different country. I need to select a list with unique users with their country either equal to UK, US or BOTH.
This is the approach Im trying but it doesn't appear to be working.
select user,
case
when COUNT(*) <= 1 and count(select where = "UK") = count(*) then "UK"
when COUNT(*) <= 1 and count(select where = "US") = count(*) then "US"
when COUNT(*) <= 1 and count(select where = "US") != count(*) then "BOTH"
END CASE
as country from users;
First time using logic like this in a query so sorry for sounding like a noob.
+------------------------------+-------------+
| user | country |
+------------------------------+-------------+
| John | US |
| Jack | US |
| John | UK |
| Joe | US |
| John | UK |
| Jacky | US |
+------------------------------+-------------+
You could do something like:
SELECT
temp.user,
if(temp.CountryList = "UK,US", "BOTH", temp.CountryList) as country
FROM
(
SELECT
user,
group_concat(DISTINCT country ORDER BY country) as CountryList
FROM USERS
GROUP BY user
) temp
Related
I have the following table:
Name Product
Bob Car
Bob Apples
Bob Pears
Bob Car
John Apples
John Pears
Whoever has bought a Product Car, I want to keep separate from everyone else. So, I create a flag:
Name Product Flag
Bob Car 1
Bob Apples 0
Bob Pears 0
Bob Car 1
John Apples 0
John Pears 0
But the problem with my flag is that even if I do a where condition and say, show me the consumer WHERE flag !=1, it'll pick Bob. Which is incorrect as Bob owns a car.
I would still like to GROUP by Product.
How do I separate the above table into two groups?
Thanks!
Use below query :-
select name from table where flag!=1
and name not in (select name from table where flag = 1)
group by name
"show me the consumer WHERE flag !=1, it'll pick Bob" that is because you are asking for rows where flag != 1. Instead you'll need something a little more complicated, like:
SELECT DISTINCT Name
FROM tableTable
WHERE Name NOT IN (SELECT Name FROM theTable WHERE Product = 'Car')
alternatively, you can do a LEFT JOIN, which may or may not be faster depending on the amount of data you have and how its values are distributed.
SELECT DISTINCT a.Name
FROM theTable a
LEFT JOIN theTable b ON a.Name = b.Name AND b.Product = 'Car'
WHERE a.Product != 'Car' AND b.Product IS NULL
;
This gets all the rows with products other than cars, and then uses the LEFT JOIN in conjunction with the IS NULL condition to find which did not also have a 'Car' row.
I think you want your table's data displayed, just with "People who bought cars" partitioned (not grouped) separately somehow - this could be done with an ORDER BY OwnsACar clause, for example.
Step 1: Identify the people who have bought cars:
SELECT DISTINCT
Name
FROM
yourTable
WHERE
Product = 'Car'
Step 2: Join on this data to generate a calculated "OwnsACar" column:
SELECT
yourTable.Name,
yourTable.Product,
ISNULL( carowners.Name ) AS OwnsACar
FROM
yourTable
LEFT OUTER JOIN
(
SELECT DISTINCT
Name
FROM
yourTable
WHERE
Product = 'Car'
) AS carowners ON carowners.Name = yourTable.Name
ORDER BY
OwnsACar ASC,
yourTable.Name ASC
You can use these two queries. The additional Flag column is not required.
-- do not have Car
SELECT *
FROM products
WHERE Name not in (SELECT DISTINCT Name
FROM products
WHERE Product='Car');
-- have Car
SELECT *
FROM products
WHERE Name in (SELECT DISTINCT Name
FROM products
WHERE Product='Car');
Illustration:
-- table
SELECT * FROM products;
+------+---------+
| Name | Product |
+------+---------+
| Bob | Car |
| Bob | Apples |
| Bob | Pears |
| Bob | Car |
| John | Apples |
| John | Pears |
+------+---------+
-- query for people that do not have Car
+------+---------+
| Name | Product |
+------+---------+
| John | Apples |
| John | Pears |
+------+---------+
-- query for people having 'Car'
+------+---------+
| Name | Product |
+------+---------+
| Bob | Car |
| Bob | Apples |
| Bob | Pears |
| Bob | Car |
+------+---------+
Try with :
SELECT `t`.`Name`, `t`.`Product`, SUM(`t`.`Flag`) as hasCar
FROM your_table t
GROUP BY `t`.`Name`
HAVING `t`.`hasCar` = 0;
Although you can go without the flag column by going :
SELECT `t`.`Name`, `t`.`Product`, SUM(IF(`t`.`Product` = 'Car', 1, 0)) as hasCar
FROM your_table t
GROUP BY `t`.`Name`
HAVING `t`.`hasCar` = 0;
I'm trying to find the most efficient and optimized way of querying husband and wife data for a search function in a finance application. The clients can be single or married.
Currently, when the data is created, there is a table for the household that shares information such as username, password, address, location, etc...
There is a separate table that stores individual information about the husband and wife in separate rows including birth dates and income.
The app has a search function where a user can search using criteria such as location, husband age range and income range and wife age range and income range and should return individual household results.
For instance, a user can search for clients that are located within 20 miles where the husband is between 50 and 60 years old and the wife is between 40 and 50 years old with an income range of $30,000 to $40,000.
The result would produce all results for singles and couples.
Here is just an idea of what the tables and results may look like. Keep in mind that the location data would actually use lat and long but for the purpose of this example, we are just using actual miles to keep it simple.
Table Users:
ID | Username | Location | Password | Email | Status
-------------------------------------------
1 | singleclient | 5 miles | 24##$#dls | user1#email.com | Single
2 | marriedclient | 7 miles | $#$sls33 | user2#email.com | Married
Table UserDetails
ID | User_ID | Gender | Name | Age | Income
----------------------------------
1 | 1 | Male | John Smith | 55 | 32000
2 | 2 | Male | Mike Jones | 53 | 37000
3 | 2 | Female | Cindy Jones | 47 | 31000
Result:
ID | Username | Distance | Status | Male Name | Female Name | Male Age | Female Age | Male Income | Female Income
----------------------------------------
1 | singleclient | 5 miles | Single | John Smith | null | 55 | null | 32000 | null
2 | marriedclient | 7 miles | Married | Mike Jones | Cindy Jones | 53 | 47 | 37000 | 31000
First, in many countries, the assumption that a marital unit consists of a single male and a single female is not true. I would try to avoid building this assumption into the data model or application.
I think you can approach this question using aggregation with a having clause:
select ud.user_id
from UserDetails ud
group by ud.user_id
having sum(case when ud.gender = 'Male' and ud.age between 50 and 60) = 1 and
sum(case when ud.gender = 'Female' and ud.age between 40 and 50 and ud.income between 30000 and 40000) = 1;
This gives you the user_ids that match. You can then format it however you like.
The above is quite generic. You might find that this version works faster:
select ud1.*, ud2.*
from UserDetails ud1 join
UserDetails ud2
on ud1.user_id = ud2.user_id
where ud1.gender = 'Male' and ud1.age between 50 and 60 and
ud2.gender = 'Female' and ud2.age between 40 and 50 and ud2.income between 30000 and 40000;
Which is faster depends on the size of your data and how indexes are set up.
You can join the same table twice under different names, and use this to populate all the fields.
You could do this by selecting the male person and the female person, but this will obviously get you in trouble when you need to deal with parties where both members are of the same sex. It might be better then to just pick the lowest ID and the highest ID in the database, or the youngest and oldest person, or whatever.
Query would look something like this (untested)
SELECT u.id, u.username, u.distance, u.status, p1.name, p2.name, p1.age, p2.age, p1.income, p2.income
FROM Users u
INNER JOIN UserDetails p1 ON u.id = p1.user_id AND p1.id = (SELECT MIN(id) FROM UserDetails WHERE user_id = u.id)
RIGHT JOIN UserDetails p2 ON u.status == 'married' AND u.id = p2.user_id AND p2.id = (SELECT MAX(id) FROM UserDetails WHERE user_id = u.id)
Adding in the "status == married" in the second (right) join will make sure that the second query will not show the same person twice, but will instead just return a row of nulls.
You'll probably need to do the query twice (so that each person can be searched as p1 and as p2) if one of the two spouses should be "50-60" and the other "40-50" or one should make $10.000 and the other $20.000, because you don't know in which order they'll come out.
Table:
id | name | country
---------------------
1 | abc | India
2 | abc | America
3 | abc | USA
4 | xyz | India
5 | xyz | America
6 | xyz | USA
QUERY tried so far:
SELECT `id`,
if(`country` = 'India',count(id),0) as object1,
if(`country` = 'America', count(id),0) as object2,
if(`country` = 'USA',count(id),0) as object3
FROM `table`
the above gives me output like this:
id | name | object1 | object2 | object3
---------------------------------------------
1 | India | 6 | 0 | 0
I want output like :
id | name | object1 | object2 | object3
---------------------------------------------
1 | India | 1 | 1 | 1
1 | America | 1 | 1 | 1
1 | USA | 1 | 1 | 1
Please someone help me out to get this output.
I think you want conditional aggregation, but it is unclear what the objects are. Based on the data, I might think:
SELECT country as name,
sum(name = 'abc') as abc,
sum(name = 'xyz') as xyz
FROM `table`
group by country;
Try this:
SELECT `id`,`country`, count(`country`) as 'CountOfCountry'
FROM `table`
GROUP by id,Country
That will give you a count of each unique country, all grouped.
This is assuming that what you are calling "objects" is really the count per country.
I am not quite sure what the third column should be because you don't have the sample data to support it. But how about the following (SQL Fiddle):
SELECT country,
SUM(CASE WHEN name = 'abc' THEN 1 ELSE 0 END) AS object1,
SUM(CASE WHEN name = 'xyz' THEN 1 ELSE 0 END) AS object2,
SUM(CASE WHEN name = 'nunya' THEN 1 ELSE 0 END) AS object3
FROM MyTable
GROUP BY country
I wasn't sure if you wanted to return the ID with your query as all the IDs in your sample output are 1, and I'm not sure how that would represent the sample data you gave, but assuming that was a typo. This is what I came up with. Here is the sql fiddle
http://sqlfiddle.com/#!6/b763d/7
select id,country,
(select count(id) from things where country='india') as object1,
(select count(id) from things where country='america') as object2,
(select count(id) from things where country='usa') as object3
from things
Now if you dont want the ID and just want things to be grouped by country then do the following.
select country,
(select count(id) from things where country='india') as object1,
(select count(id) from things where country='america') as object2,
(select count(id) from things where country='usa') as object3
from things
group by country
THIS is the table for the questions
+---------+----------+------+
| owner | species | sex |
+---------+----------+------+
| Harry | cat | f |
| Gwen | dog | m |
| Adlai | rat | m |
| Alex | bird | f |
| Harry | dog | f |
| Gwen | cat | m |
| Gwen | dog | f |
+---------+----------+------+
I have tried >
SELECT owner,MAX(count(species)) FROM pet GROUP BY owner;
did not get it.
i managed to get >
SELECT owner, count(species) FROM pets GROUP BY owner;
+---------+----------------+
| owner | count(species) |
+---------+----------------+
| Gwen | 3 |
| Adlai | 1 |
| Harry | 2 |
| Alex | 1 |
+---------+----------------+
but it needs only one...
please help!
Thanks!
1.
SELECT owner
FROM pet
WHERE species IN ('cat', 'dog')
GROUP BY owner
HAVING COUNT(*) = 2
This query assumes the owner can only have a single cat and a single dog.
1a.
SELECT p1.owner
FROM pet p1
INNER JOIN p2 ON p1.owner = p2.owner
AND p2.pet = 'dog'
WHERE p1.pet = 'cat'
GROUP BY p1.owner
This query is slightly better since it doesn't care of how many pets every owner has.
2.
SELECT owner, count(species) cnt
FROM pets
GROUP BY owner
ORDER BY count(species) DESC
LIMIT 1
PS: and try to do your homework next time by yourself ;-)
Instead of hard-coding specific species, what it looks like you are asking for is... Of the owners of pets, I want to know who has the most distinct species regardless of sexual gender (m/f). Whatever that count is, you want to list all owners who had that # of species. Start first with getting the highest distinct species count per ANY owner, ANY species...
However, by re-reading, it looks like you are considering Gwen as 3 species even though 2 are dogs, 1 is a cat, but the dogs are one male, one female... What if someone has 4 dogs and they are all male and 5 cats all female. Would that count as 2 species?
Anyhow, based on my original based on distinct SPECIES (regardless of gender), I have a solution
select
PreQuery.owner,
PreQuery.SpeciesCount,
#HighCount := if( #HighCount < PreQuery.SpeciesCount,
PreQuery.SpeciesCount, #HighCount ) as KeepCount
from
( select
owner,
count( distinct species ) as SpeciesCount
from
pets
group by
owner
order by
count( distinct species ) desc ) PreQuery,
( select #HighCount := 0 ) sqlvars
having
SpeciesCount = KeepCount
at SQL Fiddle
The first query pre-aggregates the number of DISTINCT species regardless of gender and orders with the highest count coming first. By using MySQL Variables (via #varName), I am applying the first record returned in the result set to the high count value, then keep it there for all subsequent records.
Finally, the "HAVING" clause allows it to retain only those that had that max count.. in this scenario, it had TWO people with two distinct species.
If you DO want it based on species AND sex, we can revise the query, but the principle will be the same.
Another query based on distinct species AND sex
I just changed to
count( distinct concat(species, sex) )
in both places
I've got a bunch of users in my database and I want to reset all their usernames to the first letter of their first name, plus their full last name. As you can imagine, there are some dupes. In this scenario, I'd like to add a "2" or "3" or something to the end of the username. How would I write a query to generate a unique username like this?
UPDATE user
SET username=lower(concat(substring(first_name,1,1), last_name), UNIQUETHINGHERE)
CREATE TABLE bar LIKE foo;
INSERT INTO bar (id,user,first,last)
(SELECT f.id,CONCAT(SUBSTRING(f.first,1,1),f.last,
(SELECT COUNT(*) FROM foo f2
WHERE SUBSTRING(f2.first,1,1) = SUBSTRING(f.first,1,1)
AND f2.last = f.last AND f2.id <= f.id
)),f.first,f.last from foo f);
DROP TABLE foo;
RENAME TABLE bar TO foo;
This relies on a primary key id, so for each record inserted into bar, we only count duplicates found in foo with id less than bar.id.
Given foo:
select * from foo;
+----+------+--------+--------+
| id | user | first | last |
+----+------+--------+--------+
| 1 | aaa | Roger | Hill |
| 2 | bbb | Sally | Road |
| 3 | ccc | Fred | Mount |
| 4 | ddd | Darren | Meadow |
| 5 | eee | Sharon | Road |
+----+------+--------+--------+
The above INSERTs into bar, resulting in:
select * from bar;
+----+----------+--------+--------+
| id | user | first | last |
+----+----------+--------+--------+
| 1 | RHill1 | Roger | Hill |
| 2 | SRoad1 | Sally | Road |
| 3 | FMount1 | Fred | Mount |
| 4 | DMeadow1 | Darren | Meadow |
| 5 | SRoad2 | Sharon | Road |
+----+----------+--------+--------+
To remove the "1" from the end of user names,
INSERT INTO bar (id,user,first,last)
(SELECT f3.id,
CONCAT(
SUBSTRING(f3.first,1,1),
f3.last,
CASE f3.cnt WHEN 1 THEN '' ELSE f3.cnt END),
f3.first,
f3.last
FROM (
SELECT
f.id,
f.first,
f.last,
(
SELECT COUNT(*)
FROM foo f2
WHERE SUBSTRING(f2.first,1,1) = SUBSTRING(f.first,1,1)
AND f2.last = f.last AND f2.id <= f.id
) as cnt
FROM foo f) f3)
As a two-parter:
SELECT max(username)
FROM user
WHERE username LIKE concat(lower(concat(substring(first_name,1,1),lastname), '%')
to retrieve the "highest" username for that name combo. Extract the numeric suffix, increment it, then insert back into the database for your new user.
This is racy, of course. Two users with the same first/last names might stomp on each other's usernames, depending on how things work out. You'd definitely want to sprinkle some transaction/locking onto the queries to make sure you don't have any users conflicting.
Nevermind.... I just found the dupes:
select LOWER(CONCAT(SUBSTRING(first_name,1,1),last_name)) as new_login,count(* ) as cnt from wx_user group by new_login having count(* )>1;
And set those ones manually. Was only a handful.
Inspired in the answer of unutbu: there is no need to create an extra table neither several queries:
UPDATE USER a
LEFT JOIN (
SELECT USR_ID,
REPLACE(
CONCAT(
SUBSTRING(f.`USR_FIRSTNAME`,1,1),
f.`USR_LASTNAME`,
(
(SELECT IF(COUNT(*) > 1, COUNT(*), '')
FROM USER f2
WHERE SUBSTRING(f2.`USR_FIRSTNAME`,1,1) =
SUBSTRING(f.`USR_FIRSTNAME`,1,1)
AND f2.`USR_LASTNAME` = f.`USR_LASTNAME`
AND f2.`USR_ID` <= f.`USR_ID`)
)
),
' ',
'') as login
FROM USER f) b
ON a.USR_ID = b.USR_ID
SET a.USR_NICKNAME = b.login