SELECTing the total count of multiple cells based upon a shared ID - mysql

I have two databases, one holds family names and the other holds family members.
Since separate family names (lastnames) can be shared between people not related, they are assigned a family "ID" which is shared across both databases.
familyNames : lastName | ID
memberNames : firstName | ID
I want to count how many members each family has, the output looking like:
Family Name | Members
----------------------
Johnson | 14
----------------------
Brown | 21
----------------------
White | 33
Is there a way to do this without creating a new column? Thanks.
(The familyNames holds many more columns that are irrelevant to the problem, hence the reason to have two tables)

select f.lastname, count(m.firstname) as cnt
from familynames f
left join membernames m on m.id = f.id
group by f.id, f.lastname
order by f.lastname

HERE Database names are prefixed to table names , DB1, DB2 are database names.
SELECT FN.lastName as 'Family Name', COUNT(*) as Members FROM
DB1.familyNames FN
JOIN DB2.memberNames MN
on FN.ID = MN.ID
group by FN.lastName

Related

MYSQL, is this kind of request possible?

I have persons (table person) who have 0 or N roles (tables role and personne_role).
I want to select all the persons , with the roles they have, to have this kind of result :
PHIL COLLINS | Drummer | Singer
MIKE RUTHERFORD | Singer
ION ANDERSON | Singer
MIKE JAGGER |
CARLOS SANTANA | Guitarist
......
Each line can have 0 or N roles.
To do that, I make 2 requests
the first one to get the employees (table person)
the second one to loop all the retrieved employees and retrieve each role of them (tables role and person_role)
It works BUT in the case of there are a lot of lines, it is not very efficient.
I would like the same result in 1 request.
Is it possible ?
What are the mysql keywords I must use to do that ?
Thanks for your feedback.
dominique
You could use a JOIN with a GROUP_CONCAT, something like:
SELECT person.name, role.roles
FROM person
LEFT JOIN (
SELECT person_id, GROUP_CONCAT(DISTINCT role SEPARATOR ' | ') roles
FROM person_role
GROUP BY person_id
) role ON (person.id = role.person_id)
EDIT: the fields name are just a guess, since you didn't show us the full table schema; also, if the roles are actually in a separate tale, say joined by a role_id, you'd need to add it to the subquery.

SQL - Row of IDs, fetch joined data

I have a table garages structured like this:
garageid | car1 | car2 | car3
The carN fields contain IDs from another table cars:
carN | make | color
Is there a way to get the list of colors for a specific garage in a single query?
The result would be:
garageid | color1 | color2 | color3
I realize this structure is not normalized, but the number of cars will never change. Is there a way to do this?
Storing multiple columns with ids is usually not a good approach. It is better to have one row per car. That said, you can do what you want using multiple joins:
select g.garageid, c1.color as color1, c2.color as color2, c3.color as color3
from garages g left join
cars c1
on g.car1 = c1.carid left join
cars c2
on g.car2 = c2.carid left join
cars c3
on g.car3 = c3.carid;
The left join will still return the garage even when one or more of the car ids are NULL.
In my opinion is better to have a table t_garage_cars:
garageId carId
In this manner is simple to retrive data.
If this is not possible for some reason I believe you can only use a cursor may be with a temp table

Select table entries belonging to combinations of entries in another table (mysql)

Regarding MySQL queries, how can I get all table entries that belong to combinations of entries in another table?
Background: I would like to count orders which consist of order items. Each order item has a state like 'canceled' or 'delivered'. There are partial deliveries, so that there can be both delivered and canceled orders in one order. I would like to count the net orders and I would like to know how many orders have items with more than one item status.
Order Number | Order Item | Status
X0001 | Item | delivered
X0001 | Item | delivered
X0002 | Item | delivered
X0002 | Item | canceled
X0002 | Item | delivered
X0003 | Item | delivered
I have 3 valid orders here and one order with delivered/canceled. How can I ask for all orders which have at least one delivered and one canceled item?
As I am very new to MySQL I am basically looking for the right approach. Do I need subqueries or joints for this?
Edit: First of all, sorry for the late reply. And sorry again because my question was obviously misleading.
There are three tables involved: 'order', 'order_item' and 'order_item_status'.
'order' and 'order_item' are linked through 'order_item.fk_order' and 'order.id_order'.
'order_item' and 'order_item_status' are linked through 'order_item.fk_order_item_status' and 'order_item_status.id_order_item_status'.
You have been very helpful so far but I am still a bit stuck as I do not know how to finally count by combination.
The perfect result would be something like that:
{shipped} | 34
{canceled} | 12
{shipped, canceled} | 8
{closed, canceled} | 4
{closed} | 27
... | ...
But I don't know how to deal with combinations in a query. Maybe you have some more helpful hints for me ...
Thank you very much.
I assume you have two table:
orders (list of your orders)
order_items (list of itemd of order)
two table are relationed by field fk_order.
try this:
SELECT o.id, (select count(*) from order_items i where i.status = 'delivered' and i.fk_order = o.id) as item_delivered, (select count(distinct i.status) from order_items i where i.fk_order = o.id) as status_no
FROM orders o
I assume your data are all in one table like you described. Then you want to group your data by order number and then only get the orders with both canceled and delivered ?
The request should also look like :
select * from table \
group by Order Number \
Having State="canceled" and State="delivered"
By join on the table with the same table you can select rows with different values. Then count the distinct order numbers.
SELECT COUNT(DISTINCT orig.Number)
FROM order_items AS orig
INNER JOIN order_items AS diff ON diff.Number = orig.Number AND diff.Status != orig.Status
This is quicker than using sub-queries.
Edit: Forgot to mention it's a more scalable implementation as well as you can add more order statuses without this failing.
I don't understand why you are talking about "combinations of entries in another table" ?
According to the following of your question you have one single table with - at least - 3 attributes: Order number, Order item, Status. Is that correct or are some of those attributes in another table and in that case can you elaborate ?
Otherwise I would try to use sub-requests and do something like:
SELECT T1.* FROM table T1 WHERE T1.status=canceled AND T1.order_number IN (SELECT T2.order_number FROM table T2 where T2.status=delivered)

Better solution to MySQL nested select in's

Currently I have two MySQL tables
Properties
id name
1 Grove house
2 howard house
3 sunny side
Advanced options
prop_id name
1 Wifi
1 Enclosed garden
1 Swimming pool
2 Swimming pool
As you can see table two contains specific features about the properties
When I only have max 3 options the query below worked just fine. (maybe a little slow but ok) now things have expanded somewhat and i have a max of 12 options that it is possible to search by and its causing me some major speed issues. The query below is for 8 options and as you can see its very messy. Is there a better way of doing what I'm trying to achieve?
SELECT * FROM properties WHERE id in (
select prop_id from advanced_options where name = 'Within 2 miles of sea or river' and prop_id in (
select prop_id from advanced_options where name = 'WiFi' and prop_id in (
select prop_id from advanced_options where name = 'Walking distance to pub' and prop_id in (
select prop_id from advanced_options where name = 'Swimming pool' and prop_id in (
select prop_id from advanced_options where name = 'Sea or River views' and prop_id in (
select prop_id from advanced_options where name = 'Pet friendly' and prop_id in (
select prop_id from advanced_options where name = 'Open fire, wood burning stove or a real flame fire-place' and prop_id in (
select prop_id from advanced_options where name='Off road parking')
)
)
)
)
)
)
)
Like Mike Brant suggest I would consider altering your datamodel to a limit to set and creating a column for each of these in your properties table. But some times the boss comes: "We also need 'flatscreen tv'" and then you have to go back to the DB and update the scheme and your data access layer.
A way to move this logic somehow out if the database it to use bitwise comparison. This allows you to make simple queries, but requires a bit of preprocessing before you make your query.
Judge for yourself.
I've put everything in a test suite for you here sqlfiddle
The basic idea is that each property in your table has an id that is the power of 2. Like this:
INSERT INTO `advanced_options` (id, name)
VALUES
(1, 'Wifi'),
(2, 'Enclosing Garden'),
(8, 'Swimming Pool'),
(16, 'Grill');
You can then store a single value in your properties table buy adding up the options:
Wifi + Swimming Pool = 1 + 8 = 9
If you want to find all properties with wifi and a swimming pool you then do like this:
SELECT * FROM `properties` WHERE `advanced_options` & 9 = 9
If you just wanted swimming pool this would be it:
SELECT * FROM `properties` WHERE `advanced_options` & 8 = 8
Go try out the fiddle
You really need to consider a schema change to your table. It seems that advanced options in and of themselves don't have any properties, so instead of an advanced_options table that is trying to be a many-to-many JOIN table, why not just have a property_options table with a field for each "options". Something like this
|prop_id | wifi | swimming_pool | etc..
-----------------------------------
| 1 | 0 | 1 |
| 2 | 1 | 0 |
Here each field is a simple TINYINT field with 0/1 boolean representation.
To where you could query like:
SELECT * FROM properties AS p
INNER JOIN property_options AS po ON p.id = po.prop.id
WHERE wifi = 1 AND swimming_pool = 1 ....
Here you would just build your WHERE clause based on which options you are querying for.
There actually wouldn't be any need to even have a separate table, as these records would have a one-to-one relationship with the properties, so you could normalize these fields onto you properties table if you like.
Join back to the advanced_options table multiple times. Here's a sample with 2 (lather, rinse, repeat).
select o1.prop_id
from advanced_options o1
inner join advanced_options o2 on o1.prop_id = o2.prop_id and o2.name = "WiFi"
where o1.name = 'Within 2 miles of sea or river'
Could you do something like this?:
select p.*,count(a.prop_id) as cnt
from properties p
inner join advanced_options a on a.prop_id = p.id
where a.name in ('Enclosed garden','Swimming pool')
group by p.name
having cnt = 2
That query would get all the properties that have ALL of those advanced_options...
I would also suggest normalizing your tables by creating a separate table Called Advanced_option (id,name) where you store your unique Option values and then create a junction entity table like Property_x_AdvancedOption (fk_PropertyID, FK_AdvancedOptionID) that way you use less resources and avoid data integrity issues.

mysql query two tables

I need to query two tables like this...
table one
customers
id companyname phone
1 | microsoft | 888-888-8888
2 | yahoo | 588-555-8874
3 | google | 225-558-4421
etc...
table two
contacts
id companyid name phone
1 | 1 | sam | 558-998-5541
2 | 1 | john | 558-998-1154
3 | 3 | larry | 111-145-7885
4 | 3 | dave | 558-998-5254
5 | 2 | sam | 558-997-5421
I need to query both tables.
So if I search for sam
it should return a list of companies with the contacts
microsoft 888-888-8888
sam 558-998-5541
john 558-998-1154
yahoo 588-555-8874
sam 558-997-5421
so it returns all company with sam and all contacts with it....
same is if I would search 'microsoft' it should return without yahoo
microsoft 888-888-8888
sam 558-998-5541
john 558-998-1154
and if I search "558-998-1154" it should return like this...
microsoft 888-888-8888
sam 558-998-5541
john 558-998-1154
I hope this is clear....
Here is my current query:
SELECT * FROM
customers, customer_contacts
WHERE (customers.code LIKE '%#URL.qm1#%'
or customers.COMPANY LIKE '%#URL.qm1#%'
or customers.phone LIKE '%#URL.qm1#%'
or customers.contact LIKE '%#URL.qm1#%'
or customers.name LIKE '%#URL.qm1#%'
or customers.address1 LIKE '%#URL.qm1#%')
AND (customers.id = customer_contacts.cid
and customer_contacts.deleted = 0)
this returns only those who have a contact...
I would need
it to return the ones without contacts as well.
This is a sticky problem, one that I almost want to say "don't try to do this is one query".
I approach SQL queries like this from a programming perspective, as I feel the results tend to be less "magical". (A property I see in too many queries — it seems SQL queries these days are written using monkeys at keyboards…)
Figure out which company IDs we want to list. This is the union of these two things:
Any "people" results matched on name or number
Any "company" results matched on name or number
List out the number for that company, and the people as well.
Let's do #2 first:
SELECT
companyname AS name,
phone
FROM
customers
WHERE id IN (company_ids we want)
UNION
SELECT
name, phone
FROM
contacts
WHERE companyid IN (company_ids we want)
Since "company_ids we want" is going to be a query, rearrange this to boil it down to just 1 occurrence:
SELECT
name, phone
FROM
(
SELECT
id AS companyid,
companyname AS name,
phone
FROM
customers
UNION
SELECT companyid, name, phone FROM contacts
) AS entities
WHERE
companyid IN (company_ids we want)
Now, to fill in the fun part, we need to answer #1:
Part #1.1 is:
SELECT companyid FROM contacts WHERE name = $search OR number = $search;
Part #1.2 is:
SELECT id AS companyid FROM customers WHERE companyname = $search OR number = $search;
(Note that $search is our input — parameterized queries differ greatly from one SQL vendor to the next, so replace that syntax as needed.)
Put the UNION of those two in the IN, and we're done:
SELECT
name, phone
FROM
(
SELECT
id AS companyid,
companyname AS name,
phone
FROM
customers
UNION
SELECT companyid, name, phone FROM contacts
) AS entities
WHERE
companyid IN (
SELECT companyid FROM contacts WHERE name = $search OR phone = $search
UNION
SELECT id AS companyid FROM customers WHERE companyname = $search OR phone = $search
)
;
And pray the database can figure out a query plan that performs this in a reasonable amount of time. Sure you don't want to roundtrip to the DB a few times?
Note the methodology: We determined what we wanted ("the names/phones for customers/contacts matching certain companyids") and then figured out the missing piece ("which company ids?"). This comes from the fact that once you match a particular person in a company (say, sam), you want everyone from that company, plus the company, or everything with that company ID. Knowing that, we get our outer query (#2), and then we just need to figure out how to determine which companies we're interested in.
Note that these won't (and SQL queries, without an ORDER BY don't) give the queries back in your rather fancy order. You can add a helper column to the inner query, however, and accomplish this:
SELECT
name, phone
FROM
(
SELECT
0 AS is_person,
id AS companyid,
companyname AS name,
phone
FROM
customers
UNION
SELECT 1 AS is_person, companyid, name, phone FROM contacts
) AS entities
WHERE
companyid IN (
SELECT companyid FROM contacts WHERE name = $search OR phone = $search
UNION
SELECT id AS companyid FROM customers WHERE companyname = $search OR phone = $search
)
ORDER BY
companyid, is_person, name
;
You can also use the is_person column (if you add it to the SELECT) if you need to segment the results in whatever gets this query's results.
(And if you end up using queries of this length, please, for the love of God, -- comment them!)