I have two tables:
Table A:
id | name
Table B:
id | hash | owners_id
owners_id contains the ids from table A.
Example:
Table A:
id | name
1 | James
2 | Jonas
Table B:
id | hash | owners_id
1 | j28sj | 1,2
Expect Result:
James | j28sj
Jonas | j28sj
Because both contain the ownerds_id
I'm trying to make a query that selects all the names from table A associates with table B owners_id column.
SELECT
A.name,
B.hash
FROM
A
left JOIN B ON
B.owners_id LIKE CONCAT('%', A.id, '%')
Note: The database you designed is poorly designed and it may not work it you have owners_id like 1,11,111 .so either you need to make seperate table with many to many relation or put leading zeros like 001,011,111
There are a couple ways to do this. If you want to keep owners_id as a comma-separated string, it's a bit messy. You need to first parse the string into a list of integers to form the join condition:
SELECT A.name, B.hash FROM A
LEFT JOIN B
ON find_in_set(A.id,B.owners_id) <> 0;
You may want to consider letting owners_id be an integer foreign key to Table A, if you can change your schema.
Here's a working SQL fiddle:
http://sqlfiddle.com/#!9/320477/4
Related
I have inherited a table with information about some groups of people in which one field which contains delimited data, with the results matched to another table.
id_group Name
-----------------------
1 2|4|5
2 3|4|6
3 1|2
And in another table I have a list of people who may belong to one or more groups
id_names Names
-----------------------
1 Jack
2 Joe
3 Fred
4 Mary
5 Bill
I would like to perform a select on the group data which results in a single field containing a comma or space delimited list of names such as this from the first group row above "Joe Fred Bill"
I have looked at using a function to split the delimited string, and also looked at sub queries, but concatenating the results of sub queries quickly becomes huge.
Thanks!
As implied by Strawberry's comment above, there is a way to do this, but it's so ugly. It's like finishing your expensive kitchen remodel using duct tape. You should feel resentment toward the person who designed the database this way.
SELECT g.id_group, GROUP_CONCAT(n.Names SEPARATOR ' ') AS Names
FROM groups AS g JOIN names AS n
ON FIND_IN_SET(n.id_names, REPLACE(g.Name, '|', ','))
GROUP BY g.id_group;
Output, tested on MySQL 5.6:
+----------+---------------+
| id_group | Names |
+----------+---------------+
| 1 | Joe Mary Bill |
| 2 | Fred Mary |
| 3 | Jack Joe |
+----------+---------------+
The complexity of this query, and the fact that it will be forced to do a table-scan and cannot be optimized, should convince you of what is wrong with storing a list of id's in a delimited string.
The better solution is to create a third table, in which you store each individual member of the group on a row by itself. That is, multiple rows per group.
CREATE TABLE group_name (
id_group INT NOT NULL,
id_name INT NOT NULL,
PRIMARY KEY (id_group, id_name)
);
Then you can query in a simpler way, and you have an opportunity to create indexes to make the query very fast.
SELECT id_group, GROUP_CONCAT(names SEPARATOR ' ') AS names
FROM groups
JOIN group_name USING (id_group)
JOIN names USING (id_name)
Shadow is correct. Your primary problem is the bad design of relations in the database. Typically one designs this kind of business problems as a so-called M:N relation (M to N). To accomplish that you need 3 tables:
first table is groups that has a GroupId field with primary key on it and a readable name field (e.g. 'group1' or whatever)
second table is people that looks exactly as you showed above. (do not forget to include a primary key in the PeopleId field also here)
third table is a bridge table called GroupMemberships. That one has 2 fields GroupId and PeopleId. This table connects the first two with each other and marks the M:N relation. One group can have 1 to N members and people can be members of 1 to M groups.
Finally, just join together the tables in the select and aggregate:
SELECT
g.Name,
GROUP_CONCAT(p.Name ORDER BY p.PeopleId DESC SEPARATOR ';') AS Members
FROM
Groups AS g
INNER JOIN GroupMemberships AS gm ON g.GroupId = gm.GroupId
INNER JOIN people AS p ON gm.PeopleId = p.PeopleId
GROUP BY g.Name;
I want to aggregate data from table A and B into one output. Problem I am facing is that some single records are needed from table A (where is some ID in a column connected with table B containing multiple records with the same ID). I want to data in one column in table B and add this value to related ID from table A.
I have already tried with joins but it is not working for me still. Can you please take a look at this code?
select r.variable, s.variable_1 s.variable_2, sum(r.sum),
from table1 r
join table2 s on r.variable = s.variable
where some_cirrcumstances
group by r.variable ,s.variable_1
order by r.variable ,s.variable_1;
Regards
Edit: please keep an eye on this translation
Please find an example what results I want:
Data in table A:
ID | variable_1 | variable_2 | Description
There is a lot of unique rows and I want to combine it with data from table B which looks like:
Data in table B:
Week_1 | ID | other_variable_1 | our_variable
Week_1 | ID | other_variable_1 | our_variable
Week_1 | ID | other_variable_1 | our_variable
ID is a connector between but I dont know how to combine these data. We can have multiple rows for one ID and sum of column per ID is needed.
Sorry if my question seems unclear, I'll try to explain.
I have a column in a row, for example /1/3/5/8/42/239/, let's say I would like to find a similar one where there is as many corresponding "ids" as possible.
Example:
| My Column |
#1 | /1/3/7/2/4/ |
#2 | /1/5/7/2/4/ |
#3 | /1/3/6/8/4/ |
Now, by running the query on #1 I would like to get row #2 as it's the most similar. Is there any way to do it or it's just my fantasy? Thanks for your time.
EDIT:
As suggested I'm expanding my question. This column represents favourite artist of an user from a music site. I'm searching them like thisMyColumn LIKE '%/ID/%' and remove by replacing /ID/ with /
Since you did not provice really much info about your data I have to fill the gaps with my guesses.
So you have a users table
users table
-----------
id
name
other_stuff
And you like to store which artists are favorites of a user. So you must have an artists table
artists table
-------------
id
name
other_stuff
And to relate you can add another table called favorites
favorites table
---------------
user_id
artist_id
In that table you add a record for every artist that a user likes.
Example data
users
id | name
1 | tom
2 | john
artists
id | name
1 | michael jackson
2 | madonna
3 | deep purple
favorites
user_id | artist_id
1 | 1
1 | 3
2 | 2
To select the favorites of user tom for instance you can do
select a.name
from artists a
join favorites f on f.artist_id = a.id
join users u on f.user_id = u.id
where u.name = 'tom'
And if you add proper indexing to your table then this is really fast!
Problem is you're storing this in a really, really awkward way.
I'm guessing you have to deal with an arbitrary number of values. You have two options:
Store the multiple ID's in a blob object in JSON format. While MySQL doesn't have JSON functions built in, there are user defined functions that will extract values for you, etc.
See: http://blog.ulf-wendel.de/2013/mysql-5-7-sql-functions-for-json-udf/
Alternatively, switch to PostGres
Add as many columns to your table as the maximum number of ID's you expect to have. So if /1/3/7/2/4/8/ is the longest entry, have 6 columns in your table. Reason this is bad: you'll have sparse columns that'll unnecessarily slow your tables.
I'm sure you could write some horrific regex to accomplish the task, but I caution on using complex regex's on enormous tables.
Perhaps my failing is not knowing the terminology, but I am looking to do perform a JOIN on two tables with another table as the foreign keys.
So the id of an item on table A is used to get the foreign key from table B, which is used to get the textual representation from table C.
TABLE A
+----+------------+
| id | name |
+----+------------+
| 1 | comment |
+----+------------+
TABLE B
+-----------+------------------+
| object_id | cat_id_ref |
+-----------+------------------+
| 1 | 2 |
+-----------+------------------+
| 1 | 3 |
+-----------+------------------+
TABLE C
+---------+----------+
| cat_id | cat_type |
+---------+----------+
| 1 | Mean |
| 2 | Nice |
| 3 | Rude |
+---------+----------+
So the question is 2 part. what is this 1 step away foreign key lookup called, terminology, and for MySQL is this sub query? Best practice for this type of JOIN
The desired result would be:
+----+------------+------------+
| id | name | cat_type |
+----+------------+------------+
| 1 | veg 1 | Nice |
+----+------------+------------+
| 1 | veg 1 | Rude |
+----+------------+------------+
This looks like a many-to-many relationship. I refer to TABLE B as an "association" table (or, sometimes a "relationship" table, in some cases, this can be called a "membership" table.)
From a database design standpoint, we do "Entity Relationship Modeling". An "entity" is a person, place, thing, concept, or event that can be uniquely identified, is of interest to the business, and we can store information about. And we have "relationships" between those entities. When we have a relationship identified, we ask the right questions, to find out how many of one entity are related to another.
In your example, it looks like B implements an association, between entities A and C.
An "A" can be related to zero, one or more "C".
A "C" can be related to zero, one or more "A".
(This is where having a suitable name to identify an entity can make the model more understandable.)
To resolve the many-to-many, we implement a third table, which we could name "A_C" or "C_A", or it could have a totally different name. (For example, the association between "COMMITTEE" and "PERSON" might be called "MEMBERSHIP").
The new table, in your example, named "B"
A "B" is related to exactly one "A"
A "B" is related to exactly one "C"
An "A" is related to zero, one or more "B"
A "C" is related to zero, one or more "B"
The introduction of the "B" table resolves the many-to-many relationship into two "one-to-many" relationships.
(It looks like you already have an understanding of how a foreign key goes on the table on the "many" side of the relationship, and how that refers to the primary key (or unique key) of the entity table on "one" side of the relationship: the value of the foreign key is a copy of the primary key value from the other table.)
As to the second part of your question, here's an example of a query that will return the specified resultset:
SELECT a.id
, a.name
, c.veg_type
FROM A a
LEFT
JOIN B b
ON b.object_id = a.id
LEFT
JOIN C c
ON c.veg_id = b.veg_type_ref
(There are different queries that will return the same resultset; the big differences will be in the handling of rows that are "missing" (for example, a row in table A that has no "matching" row in table B. The other differences are performance, depending on cardinality, selectivity, available indexes, etc.)
These are all JOINs; there's no need to introduce a subquery to get the specified resultset.
It sounds like you're looking for something like this.
select A.*, C.veg_type
from A
inner join B
on A.id = B.object_id
inner join C
on C.veg_id = B.veg_id_ref
Basically, just a one - to - many relationship
Can a veg have more than 1 type? This example seems too simple to require the middle table.
select id,name, veg_type
from TableA
inner join TableB on Tablea.id = tableb.object_id
inner join TableC on tableb.veg_id_ref = tablec.id
I've got a core table and and 3 tables that extend the 'core' table in different ways.
I'm working with MLS data and I have a 'common' table that contains information common to all mls listings and then a table that has specifically "residential" information, one for "commercial",etc... I have been using mls number to join a single table when I know a listing when the property type is known, but for searching I want to join all of them and have the special fields available for search criteria (not simply searching the common table).
What type of join will give me a dataset that will contain all listings (including the extended fields in the idx tables) ?
For each Common table record there is a single corresponding record in ONLY ONE of the idx tables.
___________
| |
| COMMON |
| |
|___________|
_|_
|
___________________|_____________________
_|_ _|_ _|_
_____|_____ _____|______ ____|______
| | | | | |
| IDX1 | | IDX2 | | IDX3 |
| | | | | |
|___________| |____________| |___________|
If you want everything in one row, you can use something like this format. Basically it gives you all the "Common" fields, then the other fields if there is a match otherwise NULL:
SELECT Common.*,
Idx1.*,
Idx2.*,
Idx3.*
FROM Common
LEFT JOIN Idx1
ON Idx1.MLSKey = Common.MLSKey
LEFT JOIN Idx2
ON Idx2.MLSKey = Common.MLSKey
LEFT JOIN Idx3
ON Idx3.MLSKey = Common.MLSKey
Bear in mind it's better to list out fields than to use the SELECT * whenever possible...
Also I'm assuming MySQL syntax is the same as SQL Server, which is what I use.
I have a similar set up of tables where the table 'jobs' is the core table.
I have this query that selects certain elements from each of the other 2 tables:
SELECT jobs.frequency, twitterdetails.accountname, feeds.feed
FROM jobs
JOIN twitterdetails ON twitterdetails.ID = jobs.accountID
JOIN feeds ON jobs.FeedID = feeds.FeedID
WHERE jobs.username ='".$currentuser."';");
So, as you can see, no specific JOIN, but the linking fields defined. You'd probably just need an extra JOIN line for your set up.
Ugly solution / poor attempt / may have misunderstood question:
SELECT common.*,IDX1.field,NULL,NULL FROM COMMON
LEFT JOIN IDX1 ON COMMON.ID = IDX1.ID
WHERE TYPE="RESIDENTIAL"
UNION ALL
SELECT common.*,NULL,IDX2.field,NULL FROM COMMON
LEFT JOIN IDX2 ON COMMON.ID = IDX2.ID
WHERE TYPE="RESIDENTIAL"
UNION ALL
SELECT common.*,NULL,NULL,IDX3.field FROM COMMON
LEFT JOIN IDX3 ON COMMON.ID = IDX3.ID
WHERE TYPE="INDUSTRIAL"
Orbit is close. Use inner join, not left join. You don't want common to show up in the join if it does not have a row in idx.
You MUST union 3 queries to get the proper results assuming each record in common can only have 1 idx table. Plug in "NULL" to fill in the columns that each idx table is missing so they can be unioned.
BTW your table design is good.