SQL - Row of IDs, fetch joined data - mysql

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

Related

SQL Validate a column with the same column

I have the following situation. I have a table with all info of article. I will like to compare the same column with it self. because I have multiple type of article. Single product and Master product. the only way that I have to differences it, is by SKU. for example.
ID | SKU
1 | 11111
2 | 11112
3 | 11113
4 | 11113-5
5 | 11113-8
6 | 11114
7 | 11115
8 | 11115-1-W
9 | 11115-2
10 | 11116
I only want to list or / and count only the sku that are full unique. follow th example the sku that are unique and no have variant are (ID = 1, 2, 6 and 10) I will want to create a query where if 11113 are again on the column not cout it. so in total I will be 4 unique sku and not "6 (on total)". Please let me know. if this are possible.
Assuming the length of master SKUs are 5 characters, try this:
select a.*
from mytable a
left join mytable b on b.sku like concat(a.sku, '%')
where length(a.sku) = 5
and b.sku is null
This query joins master SKUs to child ones, but filters out successful joins - leaving only solitary master SKUs.
You can do this by grouping and counting the unique rows.
First, we will need to take your table and add a new column, MasterSKU. This will be the first five characters of the SKU column. Once we have the MasterSKU, we can then GROUP BY it. This will bundle together all of the rows having the same MasterSKU. Once we are grouping we get access to aggregate functions like COUNT(). We will use that function to count the number of rows for each MasterSKU. Then, we will filter out any rows that have a COUNT() over 1. That will leave you with only the unique rows remaining.
Take that unique list and LEFT JOIN it back into your original table to grab the IDs.
SELECT ID, A.MasterSKU
FROM (
SELECT
MasterSKU = SUBSTRING(SKU,1,5),
MasterSKUCount = COUNT(*)
FROM MyTable
GROUP BY SUBSTRING(SKU,1,5)
HAVING COUNT(*) = 1
) AS A
LEFT JOIN (
SELECT
ID,
MasterSKU = SUBSTRING(SKU,1,5)
FROM MyTable
) AS B
ON A.MasterSKU = B.MasterSKU
Now one thing I noticed from you example. The original SKU column really looks like three columns in one. We have multiple values being joined with hypens.
11115-1-W
There may be a reason for it, but most likely this violates first normal form and will make the database hard to query. It's part of the reason why such a complicated query is needed. If the SKU column really represents multiple things then we may want to consider breaking it out into MasterSKU, Version, and Color or whatever each hyphen represents.

MySQL Query which require multiple joins

I have a system that is used to log kids' their behavior. If a child is naughty it is logged as negative and if it has a well behaviour it is logged as positive.
For instance - if a child is rude it gets a 'Rude' negative and this is logged in the system with minus x points.
My structure can be seen in this sqlfiddle - http://sqlfiddle.com/#!9/46904
In the users_rewards_logged table, the reward_id column is a foreign key linked to either the deductions OR achievements table depending on the type of column.
If type is 1 is a deduction reward, if the type value is 2 is a achievement reward.
I basically want a query to list out something like this:
+------------------------------+
| reward | points | count |
+------------------------------+
| Good Work | 100 | 1 |
| Rude | -50 | 2 |
+------------------------------+
So it tallys up the figures and matches the reward depending on type (1 is a deduction, 2 is a achievement)
What is a good way to do this, based on the sqlfiddle?
Here's a query that gets the above desired results:
SELECT COALESCE(ua.name, ud.name) AS reward,
SUM(url.points) AS points, COUNT(url.logged_id) AS count
FROM users_rewards_logged url
LEFT JOIN users_deductions ud
ON ud.deduction_id = url.reward_id
AND url.type = 1
LEFT JOIN users_achievements ua
ON ua.achievement_id = url.reward_id
AND url.type = 2
GROUP BY url.reward_id, url.type
Your SQLFiddle had the order of points and type in the wrong order for the table users_rewards_logged.
Here's the fixed SQLFiddle with the result:
reward points count
Good Work 100 1
Rude -50 2
Although eggyal is correct--this is rather bad design for your data--what you ask can be done, but requires a UNION clause:
SELECT users_achievements.name, users_rewards_logged.points, COUNT(*)
FROM users_rewards_logged
INNER JOIN users_achievements ON users_achievements.achievement_id = users_rewards_logged.reward_id
WHERE users_rewards_logged.type = 2
UNION
SELECT users_deductions.name, users_rewards_logged.points, COUNT(*)
FROM users_rewards_logged
INNER JOIN users_deductions ON users_deductions.deduction_id = users_rewards_logged.reward_id
WHERE users_rewards_logged.type = 1
GROUP BY 1, 2
There's no reason NOT to combine the achievements and deductions tables and just use non-conflicting codes. If you combined the tables, then you would no longer need the UNION clause--your query would be MUCH simpler.
I noticed that you have two tables (users_deductions and users_achievements) that defines the type of reward. As #eggyal stated, you are violating the principle of orthogonal design, which causes the lack of normalization of your schema.
So, I have combined the tables users_deductions and users_achievements in one table called reward_type.
The result is in this fiddle: http://sqlfiddle.com/#!9/813d5/6

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

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

MySQL multiple intersection with self performance

For simplicity, let's say we have a table with two columns: uid (user id) and fruit, describing what kinds of fruit a user likes.
E.g.:
uid | fruit
----|------------
1 | Strawberry
1 | Orange
2 | Strawberry
2 | Banana
3 | Watermelon
and so forth.
If I want to find what kinds of fruit are common in N particular users (i.e. the intersection N times of the table with itself), the first option is to use an INNER JOIN.
SELECT DISTINCT fruit FROM Fruits f1
INNER JOIN Fruits f2 USING (fruit)
INNER JOIN Fruits f3 USING (fruit)
...
INNER JOIN Fruits fN USING (fruit)
WHERE f1.uid = 1 AND f2.uid = 2 ... AND fN.uid = M
But this kinds of looks silly to me. What if N = 10? or even 20? Is it sensible to do 20 joins? Is there some other join operation I'm missing?
Before learning the "magic" of joins, I used another method, which would apply in my current case as follows:
SELECT DISTINCT fruit FROM Fruits
WHERE uid IN (1, 2, ..., M)
GROUP BY fruit
HAVING COUNT (*) = N
It seems much more compact, but I remember somebody telling me to avoid using GROUP BY because it is slower than an INNER JOIN.
So, I guess my question really is, is there maybe a third method for doing the above? If yes/no, which one is the most efficient?
-- EDIT --
So, it seems a question has been asked before, bearing a resemblance to mine. The two answers provided, are actually the two methods I'm using.
But the question remains. Which one is really more efficient? Is there, maybe, a third one?

MySQL Query Search using Multiple Rows

Firstly I'd like to start by apologizing for the potentially miss-leading title... I am finding it difficult to describe what I am trying to do here.
With the current project I'm working on, we have setup a 'dynamic' database structure with MySQL that looks something like this.
item_details ( Describes the item_data )
fieldID | fieldValue | fieldCaption
1 | addr1 | Address Line 1
2 | country | Country
item_data
itemID | fieldID | fieldValue
12345 | 1 | Some Random Address
12345 | 2 | United Kingdom
So as you can see, if for example I wanted to lookup the address for the item 12345 I would simply do the statement.
SELECT fieldValue FROM item_data WHERE fieldID=1 and itemID=12345;
But here is where I am stuck... the database is relatively large with around ~80k rows and I am trying to create a set of search functions within PHP.
I would like to be able to perform a query on the result set of a query as quickly as possible...
For example, Search an address name within a certain country... ie: Search for the fieldValue of the results with the same itemID's as the results from the query:
'SELECT itemID from item_data WHERE fieldID=2 and fieldValue='United Kingdom'..
Sorry If I am unclear, I have been struggling with this for the past couple of days...
Cheers
You can do this in a couple of ways. One is to use multiple joins to the item_data table with the fieldID limited to whatever it is you want to get.
SELECT *
FROM
Item i
INNER JOIN item_data country
ON i.itemID = country.itemID
and fieldid = 2
INNER JOIN item_data address
ON i.itemID = country.itemID
and fieldid = 1
WHERE
country.fieldValue= 'United Kingdom'
and address.fieldValue= 'Whatever'
As an aside this structure is often referred to as an Entry Attribute Value or EAV database
Sorry in advance if this sounds patronizing, but (as you suggested) I'm not quite clear what you are asking for.
If you are looking for one query to do the whole thing, you could simply nest them. For your example, pretend there is a table named CACHED with the results of your UK query, and write the query you want against that, but replace CACHED with your UK query.
If the idea is that you have ALREADY done this UK query and want to (re-)use its results, you could save the results to a table in the DB (which may not be practical if there are a large number of queries executed), or save the list of IDs as text and paste that into the subsequent query (...WHERE ID in (...) ... ), which might be OK if your 'cached' query gives you a manageable fraction of the original table.