How to search husband and wife data with one MySQL query? - mysql

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.

Related

Merge based on "group by" groups

So I have a table called the Activities table that contains a schema of user_id, activity
There is a row for each user, activity combo.
Here is a what it might look like (empty rows added to make things easier to look at, please ignore):
| user_id | activity |
|---------|-----------|
| 1 | swimming | -- We want to match this
| 1 | running | -- person's activities
| | |
| 2 | swimming |
| 2 | running |
| 2 | rowing |
| | |
| 3 | swimming |
| | |
| 4 | skydiving |
| 4 | running |
| 4 | swimming |
I would like to basically find all other users with at least the same activities as a given input id so that I could recommend users with similar activities.
so in the table above, if I wanna find recommended users for user_id=1, the query would return user_id=2 and user_id=4 because they engage in both swimming, running (and more), but not user_id=3 because they only engage in swimming
So a result with a single column of:
| user_id |
|---------|
| 2 |
| 4 |
is what I would ideally be looking for
As far as what I've tried, I am kinda stuck at how to get a solid set of user_id=1's activities to match against. Basically I'm looking for something along the lines of:
SELECT user_id from Activities
GROUP BY user_id
HAVING input_user_activities in user_x_activities
where user1_activities is just a set of our input user's activities. I can create that set using a WITH input_user_activities AS (...) in the beginning, what I'm stuck at is the user_x_activities part
Any thoughts?
To get users with the same activities, you can use a self join. Let me assume that the rows are unique:
select a.user_id
from activities a1 join
activities a
on a1.activity = a.activity and
a1.user_id = #user_id
group by a.user_id
having count(*) = (select count(*) from activities a1 where a1.user_id = #user_id);
The having clause answers your question -- of getting users that have the same activities as a given user.
You can easily get all users ordered by similarity using a JOIN (that finds all common rows) and a GROUP BY (to summarize the similarity per user_id) and finally an ORDER BY to return the most similar users first.
SELECT b.user_id, COUNT(*) similarity
FROM activities a
JOIN activities b
ON a.activity = b.activity
WHERE a.user_id = 1 AND b.user_id != 1
GROUP BY b.user_id
ORDER BY COUNT(*) DESC
An SQLfiddle to test with.

Writing complex MySQL Join query

This may be simple for some, but I cannot work it out.
I have 3 tables:
Teams, Users, Tags
Teams Users Tags
------------------- ------------------ -----------------------
userID | teamUserID userID | username userID | name | value
------------------- ------------------ -----------------------
1 | 2 1 | dan 2 | myTag | 1
1 | 3 2 | bob 2 | aTag | 2
1 | 4 3 | jon 3 | bTag | 1
4 | rob 4 | cTag | 5
Each team can have a number of users in it, and each user can own a number of tags.
I need a query which will provide a list of users in any given team, with a total number of tags they have.
So when I request results from team 1 (dan's team) it should return this:
-----------------------------------
userID | username | tagTotalValue
-----------------------------------
2 | bob | 3
3 | jon | 1
4 | rob | 5
I have this query so far, but it just gives me one record with an overall total for the whole team, rather than a list of all the users in the team separately with their totals.
SELECT username, SUM(value) tagTotalValue
FROM users u LEFT JOIN tags t
ON u.userID = t.userID
WHERE u.userID IN (SELECT teamUserID FROM teams WHERE userID = 1)
Help!
If anyone can explain a good way of working out how to build these queries, I would be very grateful to learn. Do I just need to do a mySQL course, or is there a simple method I can employ?
I need a query which will provide a list of users in any given team,
with a total number of tags they have.
This seems to have little to do with the query you have written. You should start by joining the three tables together and then aggregating. The query looks something like this:
SELECT t.teamId, u.userId, u.username, count(ta.userId) as numTags
FROM teams t JOIN
users u
ON t.teamUserID = u.UserId LEFT JOIN
tags ta
ON u.userID = ta.userID
WHERE t.teamId = #teamId -- this can be removed
GROUP BY t.teamId, u.userId, u.username;
This query makes the leap that teams has a column that identifies the team -- say teamId.

How to get hobbies in student lists with subquery mysql

well, i have 100 student lists and every student has more than a hooby.
i have 2 tables,
table name = students
attribute = student_id,name,dob,address
and
table name = hobbies
attribute = hobby_id, student_id, hoby_name.
how do i get result like below.
.student_id | name | dob | address | hobby.
1 | Jordan | 12-12-1998 | 23 avenue |reading, dota2, football
2 | Bela | 13-01-1997 | 12 hills |swimming, badminton
3 | Jack | 01-02-1999 | 07 clinton|dota2
once i try to use subquery it says "subquery returns more than 1 row".
thank u guys.
select sa.student_id,name,dob,address, GROUP_CONCAT(hoby_name)
from students_attribute sa
left join hobbies_attribute ha
on sa.student_id = ha.student_id
group by sa.student_id
This will provide to required result.

my sql questions

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

Join 2 tables, print data from 2nd table when joined rows occur

The case:
I have 2 tables, 'contracts' and 'members' tied with contract.id = members.cid.
Each contract has one main member and several secondary members (usually the children and spouse of the main member). The main member's details (name, address etc) are stored in table contracts whereas, extra members details are kept in table members. (bad logic, i know but this was a mess to begin with and i need time to redesign the db and website)
The desired output:
When I run a batch print of all contracts (lets say, every Friday) I need to also print a copy of the contract for each member, too but with the member's details on the contract instead of the main member.
The question:
How does this translate into a mysql query? Ok, its a left join, but how do I say "print data from table members instead of contracts for the joined rows"?
Main fields that occur in the 2 tables are name + surname, those should be enough for a draft query example.
Example tables and data:
contracts
-------------------------
id | name | surname |
-------------------------
1 | Tom | Jones |
2 | Jamie | Oliver |
members
--------------------------------
id | cid | name | surname |
--------------------------------
1 | 1 | Jack | Jones |
2 | 1 | Anne | Jones |
3 | 2 | Cathy | Wilson |
So the results I want shoudld be:
cid | name | surname |
--------------------------
1 | Tom | Jones |
1 | Jack | Jones |
1 | Anne | Jones |
2 | Jamie | Oliver |
2 | Cathy | Wilson |
If i write
SELECT c.name as name, c.surname as surname, m.name as name, m.surname as surname
FROM contracts c
join members m on c.id = m.cid
I simply end up with
name and name_1, surname and surname_1 but I want ALL names to fall under name and likewise for all other matching columns.
Hope this works :::
select c1.id, c1.name, c1.surname
from contracts c1
union
(Select m.id, m.name, m.surname
from
members m left join contracts c on (c.id = m.cid))
This is what I finally did and it worked (field names are different but the syntax is what matters):
SELECT u.id, u.onoma_u, u.name_u,
coalesce(u.programa, aa.programa) as programa,
coalesce(u.barcode, aa.barcode) as barcode,
coalesce(u.plan, aa.plan) as plan,
coalesce(u.im_exp, aa.im_exp) as im_exp,
coalesce(u.symb, aa.symb) as symb
FROM (SELECT a1.id, a1.onoma_u, a1.name_u, a1.programa, a1.barcode, a1.plan, a1.im_exp, a1.symb
FROM aitisi a1
UNION
SELECT a2.id, m.name, m.surname, NULL, NULL, NULL, NULL, NULL
FROM members m
JOIN aitisi a2 ON a2.id = m.symbid) u
JOIN aitisi aa ON aa.id = u.id;
I used aliases and NULLS as dummy fields to fill in the blanks.