MySQL: Multiple ids in one row from another table - mysql

This may be a bit convoluted but I'll give it a shot.
I have a table that has event data:
| id | event | from_location | to_location |
| 1 | move | 12 | 14 |
| 2 | move | 13 | 15 |
and the from location and to location are ids referenced in another able
| id | name |
| 12 | london |
| 13 | paris |
| 14 | newyork |
| 15 | tokyo |
My issue is I need to search both the first table based on the location, but using the name in the second, and I'd like to do it as simply as possible with one query.
If it was one column, I could just do a join and have the name available but since it is two, this doesn't work.
I could search for the name in the first table, then having the id, use that to search the other table - but I'd like to do it in one query.
So my question - is there a way to simply replace the ids with the corresponding name - then do the search, all in one query?
I would say one more thing - I didn't set this up. if I had, I'd have forgone the use of ids altogether and simply used the names as the keys. But now that it is how it is - lets assume I can't change this.
Thanks

If you want to check if the from or to location is a particular one:-
SELECT event_data.id, event_data.event, event_data.from_location, event_data.to_location
FROM event_data
INNER JOIN locations l1 ON event_data.from_location = l1.id
INNER JOIN locations l2 ON event_data.to_location = l2.id
WHERE l1.name = 'somewhere'
OR l2.name = 'somewhere'
If you want a particular from location and another particular to location
SELECT event_data.id, event_data.event, event_data.from_location, event_data.to_location
FROM event_data
INNER JOIN locations l1 ON event_data.from_location = l1.id
INNER JOIN locations l2 ON event_data.to_location = l2.id
WHERE l1.name = 'somewhere'
AND l2.name = 'somewhereelse'

Related

Removing Records with String Contained in Other Records using 3 tables and Joins

I previously got a great answer (thank you #Paul Spiegel) on removing records from a table whose string was contained at the end of another record. For example, removing 'Farm' when 'Animal Farm' existed) and grouped by a Client Field.
The problem is, in fact, a little more complex and spans three tables, I'd hoped I could extend the logic easily but it turns out to also be challenging (for me). Instead of one table with Client and Term, I have three tables:
Terms
Clients
Look-up-Table (LUT) where I store pairs of TermID and ClientID
I have made some progress since initially posting this question so where I stand is I made the Joins and resultant Select return the fields I want to delete from the Look-up-Table (LUT):
http://sqlfiddle.com/#!9/479c72/45
The final select being:
Select Distinct(C.Title),T2.Term From LUT L
Inner Join Terms T
On L.TermID=T.ID
Inner Join Terms T2
On T.Term Like Concat('% ', T2.Term)
Inner Join Clients C
On C.ID=L.ClientID;
I am in the process of trying to turn this into a Delete with little success.
Append this to your query:
Inner Join LUT L2
On L2.ClientID = L.ClientID
And L2.TermID = T2.ID
That will ensure, that the clients do match and you will get the following result:
| ClientID | TermID | ID | Term | ID | Term | ID | Title | ClientID | TermID |
|----------|--------|----|---------------|----|-----------|----|-------|----------|--------|
| 1 | 2 | 2 | Small Dog | 1 | Dog | 1 | Bob | 1 | 1 |
| 2 | 5 | 5 | Big Black Dog | 3 | Black Dog | 2 | Alice | 2 | 3 |
To delete the corresponding rows from the LUT table, replace Select * with Delete L2.
But deleting the terms is more tricky. Since it's a many-to-many relation, the term may belong to multiple clients. So you can't just delete them. You will need to cleanup up the table in a second statement. That can be done with the following statement:
Delete T
From Terms T
Left Join LUT L
On L.TermID = T.ID
Where L.TermID Is Null
Demo: http://sqlfiddle.com/#!9/b17659/1
Note that in this case the term Medium Dog will also be deleted, since it doesn't belong to any client.

Trying to reorder the sequence of which MySQL determines which entry is distinct by

Consider I have a Zoo Parent assigned to a Zoo Animal, I would like to get the ID's of all Zoo Parent which are distinct by the Zoo Animal's serial tag, What I'd want to do is:
SELECT
zoo_parent.id
FROM
zoo_parent
INNER JOIN
zoo_animal ON (zoo_parent.animal_id = zoo_animal.id)
WHERE
zoo_animal.is_active = "Y"
GROUP BY
zoo_animal.serial_tag
The dilemma now is that I have a bunch of Zoo Animals with the same serial_tag because the owners of the software do not want to use historical records, instead they just save a new entry to the database using the same serial_tag. Their request to me is in a table that looks like this:
|------------------|------------------|-------------------|
| Zoo Parent.id | Zoo Animal.id | Zoo Animal.serial |
|------------------|------------------|-------------------|
| 1 | 1 | ABC-100 |
| 2 | 2 | ABC-200 |
| 3 | 3 | ABC-300 |
| 4 | 4 | ABC-100 |
|------------------|------------------|-------------------|
Ideally assuming running number for primary-key values, I would like to GROUP BY zoo_animal.serial_tag and get the zoo_parent.id = 4 instead of zoo_parent.id = 1` (Latest "version" of this Parent's Animal's Serial Number). Note, they do not want me to change the schema in any way.
if you want the max id, use the MAX function:
SELECT
MAX(zoo_parent.id) as max_id
FROM
zoo_parent
INNER JOIN
zoo_animal ON (zoo_parent.animal_id = zoo_animal.id)
WHERE
zoo_animal.is_active = "Y"
GROUP BY
zoo_animal.serial_tag

Why does SELECT * FROM table INNER JOIN..ON show the intersecting column twice?

I'm using MySQL to join two different tables together. people and homes.
When I try to inner join the two together with the USING keyword, it gives back one column for the intersecting column (address) I want joined:
SELECT * FROM people INNER JOIN homes USING(address);
+---------------------+------------+-----------+----------+
| address | first_name | last_name | city |
+---------------------+------------+-----------+----------+
| 533 Dufferin Street | Joe | Smith | Toronto |
| 421 Yonge Street | John | Schmidt | New York |
| 90 Bayview Avenue | Mary | Poppins | Chicago |
| 800 Keele Street | Joe | Dirt | L.A |
+---------------------+------------+-----------+----------+
Whereas, when you inner join the two tables using ON keyword, it gives back two columns for address (intersecting column).
SELECT * from people INNER JOIN homes ON(people.address = homes.address);
+------------+-----------+---------------------+---------------------+----------+
| first_name | last_name | address | address | city |
+------------+-----------+---------------------+---------------------+----------+
| Joe | Smith | 533 Dufferin Street | 533 Dufferin Street | Toronto |
| John | Schmidt | 421 Yonge Street | 421 Yonge Street | New York |
| Mary | Poppins | 90 Bayview Avenue | 90 Bayview Avenue | Chicago |
| Joe | Dirt | 800 Keele Street | 800 Keele Street | L.A |
+------------+-----------+---------------------+---------------------+----------+
So I guess to sum up, why does USING result the column address being displayed once, vs. ON resulting in address being shown twice?
When you use ON people.address = home.address, it's just a coincidence that the column names are the same in both tables -- often this type of ON condition matches columns with different names. The duplicate columns are not filtered out of the result when you do this.
But when you use USING (address), the column names are required to be the same in both tables (since USING doesn't allow you to relate columns with different names). Since it's obviously redundant to have both of them, the duplicates are filtered out.
see MySQL Join Syntax, especially in section 'Join Processing Changes in MySQL 5.0.12'. USING or NATURAL JOIN treat the join attribute always as the same such that one of both is redundant. Different from previous behaviour, since 5.0.12 'the redundant column is eliminated and the column order is correct according to standard SQL'.
In this query
SELECT * from people INNER JOIN homes ON(people.address = homes.address);
there are two adresses in the results, namely people.address and homes.address. In case of an inner join the two have the same value. In case of another join type (outer or cross join) they wouldn't.
In the other query
SELECT * FROM people INNER JOIN homes USING(address);
you merge the two to a single unqualified address. What you gain is that the join criteria is simpler to write (but with more complex queries there come situations when you need a qualifier and then you go back to ON again).
USING is especially helpful with multiple full outer joins*:
select col, a.colx, b.coly, c.colz
from a
full outer join b using (col)
full outer join c using (col);
versus
select coalesce(a.col,b.col,c.col) as col, a.colx, b.coly, c.colz
from a
full outer join b on b.col = a.col
full outer join c on c.col = coalesce(a.col,b.col);
*) Full outer joins are not supported in MySQL though.
in your controller
$data['address'] = $this->yourmodel->getAdd();
Try to use this in your model.
public function getAdd() {
$this->db-> select ( 'p.first_name' , 'p.last_name', a.address as add');
$this->db->join( 'homes a' , a.address = p.address');
$query = $this->db-> get ('people p');
$query-> return ->result_array();
}

Mysql: how to return value or empty string from column ids in table (multi table)

I'M trying to extract all information into my table, but I need to change id, when available, to the name into another table.
I have 1 table like that:
|------------------------------|
|-id-|-systems-|-remote-|-deco-|
| 1 | NULL | 3 | |
| 2 | 21 | NULL | 2 |
|-------------------------------
each column like "systems" / "remote" / "deco" refer to an id into another table
I know how to use INNER JOIN. But if I use that, I got an empty result because the value need to be appears into the others tables.
ex.:
SELECT qd.id,s.name as systems,r.name as remote, d.name as deco
FROM `quote_data` qd
INNER JOIN systems s ON qd.systems=s.id
INNER JOIN remote r ON qd.remote=r.id
INNER JOIN deco d ON qd.deco=d.id
I got empty result.
In the best words, I need to do something like:
|------------------------------|
|-id-|-systems-|-remote-|-deco-|
| 1 | | R42 | |
| 2 | GTV | | B21 |
|-------------------------------
Also, I use innoDB table
Any Idea how to fix that?

Finding shared list IDs in a MySQL table using bitwise operands

I want to find items in common from the "following_list" column in a table of users:
+----+--------------------+-------------------------------------+
| id | name | following_list |
+----+--------------------+-------------------------------------+
| 9 | User 1 | 26,6,12,10,21,24,19,16 |
| 10 | User 2 | 21,24 |
| 12 | User 3 | 9,20,21,26,30 |
| 16 | User 4 | 6,52,9,10 |
| 19 | User 5 | 9,10,6,24 |
| 21 | User 6 | 9,10,6,12 |
| 24 | User 7 | 9,10,6 |
| 46 | User 8 | 45 |
| 52 | User 9 | 10,12,16,21,19,20,18,17,23,25,24,22 |
+----+--------------------+-------------------------------------+
I was hoping to be able to sort by the number of matches for a given user id. For example, I want to match all users except #9 against #9 to see which of the IDs in the "following_list" column they have in common.
I found a way of doing this through the "SET" datatype and some bit trickery:
http://dev.mysql.com/tech-resources/articles/mysql-set-datatype.html#bits
However, I need to do this on an arbitrary list of IDs. I was hoping this could be done entirely through the database, but this is a little out of my league.
EDIT: Thanks for the help everybody. I'm still curious as to whether a bit-based approach could work, but the 3-table join works nicely.
SELECT a.following_id, COUNT( c.following_id ) AS matches
FROM following a
LEFT JOIN following b ON b.user_id = a.following_id
LEFT JOIN following c ON c.user_id = a.user_id
AND c.following_id = b.following_id
WHERE a.user_id = ?
GROUP BY a.following_id
Now I have to keep convincing myself not to prematurely optimize.
If you normalised your following_list column into a separate table with user_id and follower_id, then you'd find that COUNT() was extremely easy to use.
You'd also find the logic for selecting a list of followers, or a list of user's being followed much easier
Your problem would be simplified if you could split your following_list column off into a child table, e.g.
TABLE id_following_list:
id | following
--------------
10 | 21
10 | 24
46 | 45
...| ...
You can read more here.
Normalize the table, drop the column following_list, create a table following:
user_id
following_id
Which leads to the easy-peasy query (untested, you get the point):
SELECT b.user_id, COUNT(c.following)
FROM following a
JOIN following b -- get followings of <id>
ON b.following_id = a.following_id
AND b.user_id = a.following_id
JOIN following c -- get all (other) followings of <id> again, match with followings of b
ON b.following_id = c.following_id
AND c.user_id = a.user_id
WHERE a.user_id = <id>
GROUP BY b.user_id
ORDER BY COUNT(b.following) DESC
Performance may very well very based on indexes & size of dataset, maybe add a 'similarity' column which is updated at regular intervals or changes just for fast data retrieval.