I want to search by name1 or name2 from two tables in a database.
IMPORTANT: Only some names have a secondary name2 associated with them.
The tables n1, and n2 are as follows:
table n1
|---------------*----------------*-----------------|
| id | name1 | n2_id |
|---------------*----------------*-----------------|
| 1 | Joseph | 1 |
| 2 | David | NULL |
| 3 | James | 2 |
|---------------*----------------*-----------------|
table n2
|---------------*----------------|
| id | name2 |
|---------------*----------------|
| 1 | Joe |
| 2 | Jim |
|---------------*----------------|
(I know that I could have just created a name 2 field in the n1 table, however this is just a simplification of a more complex system that requires this structure.)
I currently select from table n1 as follows:
SELECT id, name1, MATCH ( name1 ) AGAINST ( "+joe*") AS score
FROM n1
WHERE MATCH ( name1 ) AGAINST ( "+joe*" in boolean mode)
order by score desc;
This works very efficiently with a very large table.
However, I'd like to select from n1 and n2, MATCHING name1 or name2 (when it exists) against a search string as if both tables were combined.
The issue is that I have had problems with efficiency of a select that orders by a combined score when there are different indexes across multiple tables.
This question isn't really just about how to construct the SELECT. It's about how to construct the SELECT in such a way that the indexes are used efficiently. (the tables are very large)
Try this
SELECT
n1.id, name1, n2_id, n2.id, name2
FROM n1 LEFT OUTER JOIN n2 ON
n2.id = n2_id
WHERE name1 LIKE '%j%' OR name2 LIKE '%j%'
ORDER BY name1;
I used LEFT OUTER JOIN, due to
"Only some names have a secondary name2 associated with them."
I combined two tables based on n2_id against id field in n2.
I used mysql LIKE for String Comparison.
Here is the SQLFIDDLE
Related
I have inherited a table where one column is a comma-separated list of primary keys for a different table:
id | other_ids | value
---|-----------|-------
1 | a,b,c | 100
2 | d,e | 200
3 | f,g | 3000
I would like to convert this table to one where each other_id gets a column of its own:
id | other_id
---|---------
1 | a
1 | b
1 | c
2 | d
2 | e
3 | f
3 | g
However, I cannot think of a way to do this?
The table is > 10 GB in size, so I would like to do this inside the database, if possible.
first time post, please be kind.
Try this
select id,SUBSTRING_INDEX(other_ids,',',1) as other_id from reverseconcat
UNION
select id,SUBSTRING_INDEX(SUBSTRING_INDEX(other_ids,',',2),',',-1) as other_id from reverseconcat
UNION
select id,SUBSTRING_INDEX(SUBSTRING_INDEX(other_ids,',',3),',',-1) as other_id from reverseconcat
order by id
Although I cant really take any credit. Found this on http://www.programering.com/a/MzMyUzNwATg.html
Unsure how you will go on a huge dataset. Also you will need to add more unions if the other_ids are > 3
If you have the other table, then you can use a join and find_in_set():
select t.id, ot.pk as other_id
from t join
othertable ot
on find_in_set(ot.pk, t.other_ids) > 0;
I'm trying to export multiple MySQL tables into a single CSV file, these tables have different number of columns and have nothing in common. An example is below:
table1:
ID| Name
1 | Ted
2 | Marry
null| null
table2:
Married| Age | Phone
Y | 35 | No
N | 25 | Yes
Y | 45 | No
The result that I want to get is:
ID| Name | Married | Age | Phone
1 | Ted | Y | 35 | No
2 | Marry | N | 25 | Yes
null| null | Y | 45 | No
Is it possible to do using MySQL commands? I have tried all types of join but it doesn't give me the result that I need.
Try this query:
SELECT * FROM
(SELECT #n1:=#n1+1 as 'index', Table1.* FROM Table1, (select #n1:=0)t)t1
natural join (SELECT #n2:=#n2+1 as 'index', Table2.* FROM Table2, (select #n2:=0)t1)t2 ;
You can see in this fiddle a working example.
In the example we generate an index column for Table1 and Table2 and natural join the 2 tables.
This way the join is done using the row position as returned by the SELECT of tables without any ORDER operator and usually this is not a good idea!
I'm not quite sure you understand what you are asking but sure you can join two tables without really caring on which rows will be matched:
SET #i1=0;
SET #i2=0;
SELECT * INTO OUTFILE 'xyz' FIELDS TERMINATED BY ','
FROM (SELECT #i1:=#i1+1 as i1, table1.* FROM table1) AS a
JOIN (SELECT #i2:=#i2+1 as i2, table2.* FROM table2) b ON (a.i1=b.i2);
I have 2 table in the database: mainTable and classificationTable:
mainTable:
id | classification_id | name
---------------------------------------
1 | 1 | Name1
2 | 2,3,4 | Name2
3 | 1,4 | Name3
4 | 4 | Name4
classificationTable:
classification_id | class_name
---------------------------------------
1 | Class Name1
2 | Class Name2
3 | Class Name3
4 | Class Name4
I want to get a select for example for row with ID 3 from mainTable like:
id = 3
class_name = Class Name1, Class Name4
Name = Name3
I try this select, but this return only first elemnt from array (fore exempla for row with ID 3, this return only Class Name1)
SELECT i.*,
(SELECT GROUP_CONCAT(cl.class_name) FROM classificationTable as cl WHERE cl.classification_id IN(i.classification_id)) as class_name
FROM mainTable as i;
Help PLZ.
Yes, you have a poorly designed database. But you can do what you want.
This requires two steps. The first is to join the mainTable to the classificationTable by looking for each classification_id in the list in the mainTable. You can do this using the like operator.
The second step is to bring all the class names back into one column. This is handled using group_concat.
The following query accomplishes this (although it is not tested, so it might have a typo):
select mt.id, mt.name, group_concat(ct.class_name separator ', ')
from mainTable mt join
classificationTable ct
on concat(',', classification_id, ',') like concat('%,', ct.classification_id, ',%')
group by mt.id, mt.name
The problem is your mainTable, which not only violates the 1st normal form but goes even beyond because it concatenates values in the same field (textbook example of a 1NF violation often includes multiple fields, which is bad enough).
The classification_id column should not be a concatenation of values but you should have a new table where you have a separate row for each classification_id and it should be tied into mainTable by id.
E.g.
id | classification_id
---------------------------------------
1 | 1
2 | 2
2 | 3
2 | 4
3 | 1
3 | 4
4 | 4
Once you've done that, your query will be much easier.
I have a table with the format:
Id | Loc |
-------|-----|
789-A | 4 |
123 | 1 |
123-BZ | 1 |
123-CG | 2 |
456 | 2 |
456 | 3 |
789 | 4 |
I want to exclude certain rows from the result of query based on whether a duplicate exists. In this case, though, the definition of a duplicate row is pretty complex:
If any row returned by the query (let's refer to this hypothetical row as ThisRow) has a counterpart row also contained within the query results where Loc is identical to ThisRow.Loc AND Id is of the form <ThisRow.Id>-<an alphanumeric suffix> then ThisRow should be considered a duplicate and excluded from the query results.
For example, using the table above, SELECT * FROM table should return the results set below:
Id | Loc |
-------|-----|
789-A | 4 |
123-BZ | 1 |
123-CG | 2 |
456 | 2 |
456 | 3 |
I understand how to write the string matching conditional:
ThisRow.Id REGEXP '^PossibleDuplicateRow.Id-[A-Za-z0-9]*'
and the straight comparison of Loc:
ThisRow.Loc = PossibleDuplicateRow.Loc
What I don't understand is how to form these conditionals into a (self-referential?) query.
Here's one way:
SELECT *
FROM myTable t1
WHERE NOT EXISTS
(
SELECT 1
FROM myTable t2
WHERE t2.Loc = t1.Loc
AND t2.Id LIKE CONCAT(t1.Id, '-%')
)
SQL Fiddle example
Or, the same query using an anti-join (which should be a little faster):
SELECT *
FROM myTable t1
LEFT OUTER JOIN myTable t2
ON t2.Loc = t1.Loc
AND t2.Id LIKE CONCAT(t1.Id, '-%')
WHERE t2.Id IS NULL
SQL Fiddle example
In the example data you give, there are no examples of duplicate locs not being on duplicate rows. For example, you don't have a row "123-AZ, 1", where the prefix row "123, 1" would conflict with two rows.
If this is a real characteristic of the data, then you can eliminate dups without a self join, by using aggregation:
select max(id), loc
from t
group by (case when locate(id, '-') = 0 then id
else left(id, locate(id, '-') - 1)
end), loc
I offer this because an aggregation should be much faster than a non-equijoin.
Hi I have one big problem with one MYSQL search.
My database TABLE looks like this :
+------+----------+------+
| id | keywords | file |
+------+----------+------+
At keywords there are many keywords for each entry seperated with comas. (keyword1,keyword2...).
At PHP array there are listed some keywords (5-10).
And my search must get all DB entries which got atleast 3 from those keywords.
Its not required to got all of those words! But it can't work and with just one.
Can somebody help me with that query? I don't got good idea how to make it.
That's a challenge. The brute force method would be to use a UNION in a subquery with a count.
For example,
select id, file, count(*) from
(select distinct id, file
from file_table
where FIND_IN_SET(keyword1, keywords)
UNION ALL
select distinct id, file
from file_table
where FIND_IN_SET(keyword2, keywords)
UNION ALL
select distinct id, file
from file_table
where FIND_IN_SET(keyword3, keywords)
UNION ALL
select distinct id, file
from file_table
where FIND_IN_SET(keyword4, keywords)
.... MORE UNION ALL ....) as files
group by id, file
having count(*) >= 3
More efficiently, you could have a separate table with keywords and ID, one keyword/ID combo per row. This would eliminate the wildcard search and make the query more efficient.
The next step would be to go to something like ElasticSearch and filter on the score of the result.
If you had this setup:
table files:
+------+-------+
| id | file |
+------+-------+
| 1000 | foo |
| 1001 | bar |
+------+-------+
table keywords:
+----+-------+
| id | word |
+----+-------+
| 9 | lorem |
| 10 | ipsum |
+----+-------+
table filekeywords:
+----+--------+--------+
| id | fileid | wordid |
+----+--------+--------+
| 1 | 1000 | 9 |
| 2 | 1000 | 10 |
| 3 | 1001 | 10 |
+----+--------+--------+
You could find files with keywords lorem, ipsum, dolor like this:
SELECT COUNT(DISTINCT(k.word)), f.*
FROM files f
INNER JOIN filekeywords fk
ON fk.fileid = f.id
INNER JOIN keywords k
ON k.id = fk.wordid
WHERE k.word in ('lorem', 'ipsum', 'dolor')
GROUP BY f.id
HAVING COUNT(DISTINCT(k.word)) >= 3