Query two tables where one row is like another - mysql

I have two table queries.
The first table looks like:
| id | Number |
+--------+-------------+
| 1 | WDX |
| 2 | ABd32 |
| 3 | CACY |
and second like:
| id | realNumber |
+--------+-------------+
| 1 | w_WDX_zed |
| 2 | ABd32_ala |
| 3 | guava |
The output needs to looks like:
| id | output |
+--------+-------------+
| 1 | w_WDX_zed |
| 2 | ABd32_ala |
| 3 | CACY |
In the first table there are car plates, and in second there are plates_username. I need to connect them and update first table to match.
I was trying to do this like:
UPDATE `TAB_a` a, `TAB_b` b
SET a.`Number` = b.`realNumber`
WHERE a.`Number` LIKE CONCAT('%',b.`realNumber`,'%') AND a.Number <> b.`realNumber`;
But that does not work.

update plates p
inner join plate_users pu
on pu.realNumber like concat(concat('%', p.number), '%')
set p.number = pu.number
This is, however, fraught with danger -- if there are plates that are substrings of another plate you're likely to get unwanted results.
if realNumber is supposed to be , how come the first one is _? If those type weren't in there, it would be a lot easier and safer.
EDIT
Ok, after a little more info below, here are two more options you can try:
update plates p
inner join plate_users pu
on pu.number like concat('%\_', p.number)
or pu.number like concat(p.number, '\_%')
or pu.number like concat(concat('%\_', p.number), '\_%')
set p.number = pu.number
Fiddle: http://sqlfiddle.com/#!9/e8999/1
and
update plates p
inner join plate_users pu
on pu.number REGEXP concat(concat('.*_?', p.number), '_?.*')
set p.number = pu.number
Fiddle: http://sqlfiddle.com/#!9/c5bc7/1
All of the above give the desired results on your minimal dataset, but i strongly suggest backing up your data before you run any of these on your live data.
The last two options are preferable, because they require at least one underscore in the realNumber.

Related

joining on a table that references another table's column twice

I have this table Meeting in POSTGRESQL that looks like:
id | subject | time |
1 | Eat stuff| 2017-08-23 00:00:00 |
2 | Do stuff | 2017-08-28 00:00:00 |
And another table SimilarMeeting that looks like:
meetingId | similarId | score |
1 | 2 | 0.8 |
where SimilarMeeting.meetingId and SimilarMeeting.similarId are foreign keys to the Meeting table. I'm trying to generate a join statement that'd give me a result that looks like:
meetingSubject | similarSubject | score
Eat stuff | Do stuff | 0.8
I'm kinda stumped here as two inner joins don't work here since the first inner join and second inner join reference two different Meeting rows.
select "ma"."subject", "mb"."subject",
"eva"."SimilarMeeting"."similarityScore" from "eva"."SimilarMeeting"
join "eva"."Meeting" AS ma on "eva"."SimilarMeeting"."meetingId" = "ma"."id"
join "eva"."Meeting" AS mb on "eva"."SimilarMeeting"."similarId" = "ma"."id"
in the last line, in the join condition, you join with ma instead of mb.

Separating a comma separated string to a new table

I inherited a project that has comma separated strings stored in a field called 'subsector' in a table named 'com_barchan_project'. I need to change this horrible design, since it's proving to be an issue trying to parse through this field. See HERE for the full story:
| id | name | sector | subsector |
+----+------+--------+-----------+
| 1 | test | 2 | 3,4,7 |
+----+------+--------+-----------+
| 2 | door | 5 | 2 |
I have created a new table called 'com_barchan_project_subsector_join' with the required fields and would like to move the values stored in 'com_barchan_project' to this new empty table.
Can anyone help me with the SQL statement that would accomplish this?
Here's what the new 'com_barchan_project_subsector_join' table should look like:
| id | project_id | subsector_id |
+----+------------+--------------+
| 1 | 1 | 3 |
+----+------------+--------------+
| 2 | 1 | 4 |
+----+------------+--------------+
| 3 | 1 | 7 |
+----+------------+--------------+
| 4 | 2 | 2 |
Once I move over the data, I will remove the 'subsector' field from the 'com_barchan_project' table and be done with it.
Thanks for your help!!!
John
Using shorter table names for brevity/clarity; and assuming you have (or can easily make) a comprehensive subsectors table...and assuming your csv are stored in a consistent format (no spaces at least).
INSERT INTO `project_subsectors` (project_id, subsector_id)
SELECT p.id, s.id
FROM projects AS p
INNER JOIN subsectors AS s ON p.subsector = s.id
OR p.subsector LIKE CONCAT(s.id, ',%')
OR p.subsector LIKE CONCAT('%,', s.id, ',%')
OR p.subsector LIKE CONCAT('%,', s.id)
;
I can't guarantee it will be fast; I'd be surprised if it was.
ON FIND_IN_SET(s.id, p.subsector) > 0 may work as well, but I am not as familiar with the behavior of that function.

Join multiple tables with same column name

I have these tables in my MySQL database:
General table:
+----generalTable-----+
+---------------------+
| id | scenario | ... |
+----+----------+-----+
| 1 | facebook | ... |
| 2 | chief | ... |
| 3 | facebook | ... |
| 4 | chief | ... |
Facebook Table:
+----facebookTable-----+
+----------------------+
| id | expiresAt | ... |
+----+-----------+-----+
| 1 | 12345678 | ... |
| 3 | 45832458 | ... |
Chief Table:
+------chiefTable------+
+----------------------+
| id | expiresAt | ... |
+----+-----------+-----+
| 2 | 43547343 | ... |
| 4 | 23443355 | ... |
Basically, the general table holds some (obviously) general data. Based on the generalTable.scenario you can look up more details in the other two tables, which are in some columns familiar (expiresAt for example) but in others not.
My question is, how to get the joined data of generalTable and the right detailed table in just one query.
So, I would like a query like this:
SELECT id, scenario, expiresAt
FROM generalTable
JOIN facebookTable
ON generalTable.id = facebookTable.id
JOIN chiefTable
ON generalTable.id = chiefTable.id
And an output like this:
| id | scenario | expiresAt |
+----+----------+-----------+
| 1 | facebook | 12345678 |
| 2 | chief | 43547343 |
| 3 | facebook | 45832458 |
| 4 | chief | 23443355 |
However, this doesn't work, because both facebookTable and chiefTable have ambiguous column name "expiresAt". For the ease of use I want to keep it that way. The result table should also only have one column "expiresAt" that is automatically filled with the right values from either facebookTable or chiefTable.
You might want to consider adding expiredAt to your general table, and removing it from the others, to remove duplication in the schema, and to make this particular query simpler.
If you need to stick with your current schema, you can use table aliases to resolve the name ambiguity, and use two joins and a union to create the result you are looking for:
SELECT g.id, g.scenario, f.expiresAt
FROM generalTable g
JOIN facebookTable f
ON g.id = f.id
UNION ALL
SELECT g.id, g.scenario, c.expiresAt
FROM generalTable g
JOIN chiefTable c
ON g.id = c.id;
The outer join approach mentioned in another answer would also solve the problem.
One way you could accomplish it is with LEFT JOIN. In the result fields you can do something like this for common fields IF(fTbl.id IS NULL, cTbl.expiresAt, fTbl.expiresAt) AS expiresAt.

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?

MySQL Query to Match Unrelated Terms

I'm trying to construct a query that's driving me crazy. I had no idea where to start with solving it, but after searching around a bit I started playing with subqueries. Now I'm at the point where I'm not sure if that will solve my issue or, if it will, how to create one that does what I want.
Here's a very simplistic view of my current table (call it tbl_1):
---------------------------------
| row | name | other_names |
|-------------------------------|
| 1 | A | B, C |
| 2 | B | C |
| 3 | A | C |
| 4 | D | E |
| 5 | C | A, B |
---------------------------------
Some of the items I'm working with have multiple names (brand names, names in other countries, code names, etc.), but ultimately all of those different names refer to the same item. I originally was running a search query along the lines of:
SELECT * FROM tbl_1
WHERE name LIKE '%A%'
OR other_names LIKE '%A%';
Which would return rows 1 and 3. However, I quickly realized that my query should also return row 2, as A = B = C. How would I go about doing something like that? I'm open to alternative suggestions outside of a fancy query, such as constructing another table that somehow combines all the names into one row, but I figure something like that would be error prone or inefficient.
Additionally, I'm running MySQL 5.5.23 using InnoDB with other code written in PHP and Python.
Thanks!
Update 5/26/12:
I went back to my original thinking of using a subquery, but right when I thought I was getting somewhere I ran into a documented MySQL issue where the query is evaluated from the outside in and my subquery will be evaluated for every row and won't finish in a realistic amount of time. Here's what I was attempting to do:
SELECT * FROM tbl_1
WHERE name = ANY
(SELECT name FROM tbl_1 WHERE other_names LIKE '%A%' or name LIKE '%A%')
OR other_names = ANY
(SELECT name FROM tbl_1 WHERE other_names LIKE '%A%' or name LIKE '%A%')
Which returns what I want using the example table, but the aforementioned MySQL issue/bug causes the subquery to be considered a dependent query rather than an independent one. As a result, I haven't been able to test the query on my real table (~250,000 rows) as it eventually times out.
I've read that the main workaround for the issue is to use joins rather than subqueries, but I'm not sure how I would apply that to what I'm trying to do. The more I think about it, I might be better off running the subqueries independently using PHP/Python and using the resulting arrays to craft the main query that I want. However, I still think there is the potential to miss some results because the terms in the columns aren't nearly as nice as my example (some of the terms are multiple words, some have parenthesis, the other names aren't necessarily comma-separated, etc).
Alternatively, I'm thinking about constructing a separate table that will build the necessary links, something like:
| 1 | A | B, C|
| 2 | B | C, A|
| 3 | C | A, B|
but I think that's a lot easier said than done considering the data I'm working with and the non-standardized format in which it exists.
The route that I'm strongly considering at the point is to build a separate table with the links that are easily constructed (i.e. 1:1 ratio for name:other_names) so I don't have to deal with the formatting issues that exist in the other_names column. I may also eliminate/limit the use of LIKE and require users to know at least one exact name in order to simplify the results and probably increase the overall performance.
In conclusion, I hate working with input data that I have no control over.
Stumbled on this question by accident, so i don't know if my suggestion is relevant, but this looks like good usage for something like an "union-find".
The SELECT would be extremely easy and fast.
But the insert & update is relativly complex and you will probably need an in-code loop (while updated rows > 0)... and several databse calls
Example for the table:
---------------------------
| row | name | group |
|-------------------------|
| 1 | A | 1 |
| 2 | B | 1 |
| 4 | C | 1 |
| 5 | D | 2 |
| 6 | X | 1 |
| 7 | Z | 2 |
---------------------------
selecting:
SELECT name FROM tbl WHERE group = (SELECT group FROM tbl WHERE name LIKE '%A%')
inserting relation K = T: (psedu codeish..)
SELECT group as gk WHERE name = K;
SELECT group as gt WHERE name = T;
if (gk empty result) and (gt empty result) insert both with new group
---------------------------
| row | name | group |
|-------------------------|
| 1 | A | 1 |
| 2 | B | 1 |
| 4 | C | 1 |
| 5 | D | 2 |
| 6 | X | 1 |
| 7 | Z | 2 |
| 8 | K | 3 |
| 9 | T | 3 |
---------------------------
if (gk empty result) and (gt NOT empty result) insert t with group = gx.group
---------------------------
| row | name | group |
|-------------------------|
| 1 | A | 1 |
| 2 | B | 1 |
| 4 | C | 1 |
| 5 | D | 2 |
| 6 | X | 1 |
| 7 | Z | 2 |
| 8 | K | 2 |
| 9 | T | 2 |
---------------------------
(the same in the other case)
and when both not empty, update one group to be the other
UPDATE tbl1 SET group = gt WHERE group = gk
I can't think of a query, that supports unlimited depth of name identity. But if you could work with a limited number of "recursions", you might consider using a query similar to this, starting with the query you provided, you retrieve all rows with name identities:
SELECT a.* FROM tbl_1 a
WHERE a.name='A'
OR a.other_names LIKE '%A%'
UNION
SELECT b.* FROM tbl_1 a
JOIN tbl_1 b ON a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%'
WHERE a.name='A'
OR a.other_names LIKE '%A%';
This query would return row 2, but it wouldn't return any additional rows having "B" as "other_name" in your example. So you would have to union another query:
SELECT a.* FROM tbl_1 a
WHERE a.name='A'
OR a.other_names LIKE '%A%'
UNION
SELECT b.* FROM tbl_1 a
JOIN tbl_1 b ON a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%'
WHERE a.name='A'
OR a.other_names LIKE '%A%';
UNION
SELECT c.* FROM tbl_1 a
JOIN tbl_1 b ON (a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%')
JOIN tbl_1 c ON (b.other_names LIKE '%' || c.name || '%' OR c.other_names LIKE '%' || b.name || '%')
WHERE a.name='A'
OR a.other_names LIKE '%A%';
As you can see, the query would grow and accelerate rapidly with increasing depth, and it also isn't what I would call beautiful. But it might fit your needs. I'm not very experienced working with MySQL functions, but I guess you would be able to create a more elegant solution also working with unlimited depth using those. You might also consider solving the problem programmatically with Python.