MySQL multiple tables full text search - mysql

On my current project I have something like the following tables.
Customer table Task table
+----+----------------+ +----+--------------+-------------+
| id | name | | id | description | customer_id |
+----+----------------+ +----+--------------+-------------+
| 1 | teste client 1 | | 1 | do something | 1 |
+----+----------------+ +----+--------------+-------------+
| 2 | teste client 2 | | 2 | anything | 2 |
+----+----------------+ +----+--------------+-------------+
And i want to search by task description and customer name using some user keywords.
select *
from task t
inner join customer c on t.customer_id = c.id
where match(t.description, c.name)
against ('+test*+some*' IN BOOLEAN MODE);
Using the previous query i'm getting the following error Error Code: 1210. Incorrect arguments to MATCH, i know that i can not use fields from diferent tables in the same match.
But how can i have the exact same results as the previous query using more that one match?
(If someone have or knows a good article about this kind of problems, please let me know)
Updates
Follow some exptected results for the provided query and data:
when I search by test and somethe result should be only teste client 1
when I search by test and client the result should be teste client 1 and teste client 2
when I search by any the result should be teste client 2
You can check the expected result in the SQLFiddle if for example we have only one table with both name and description.

UPDATED solution SQLFiddle
set #q = 'test* some*';
select * from
(
select
c.id,
c.name,
match(c.name) against (#q IN BOOLEAN MODE) as t1_match,
match(t.description) against (#q IN BOOLEAN MODE) as t2_match
from task t
inner join customer c on t.customer_id = c.id
) as s
where s.t1_match > 0 OR s.t2_match > 0
Currently, I think your desired results logic is mutually exclusive. See my comment above.

Related

SELECT Max index for multiple column values in the same table?

I have the following database structure:
post_id | language | index
Data looks like:
1 | en | 1
1 | en | 2
1 | en | 3
1 | fr | 1
1 | fr | 2
language is a shortcode like en, fr, etc. index is an incrementing integer. There is at least one language code in the database.
I need to get the max value of index for each language.
Currently i get all languages for a post
SELECT DISTINCT(language) FROM my_table WHERE post_id = ?
and iterate then over the language array to get the Max value for each language
SELECT MAX(index) FROM my_table WHERE post_id = ? AND language = 'language_code'
Is there a way to execute just one query to achieve this result?
The result should look like:
1 | en | 3
1 | fr | 2
You can use GROUP BY to achieve this.
You can check this link for more infos about the usage of GROUP BY with MAX: SQL MAX() with group by
SELECT post_id, language, MAX(index)
FROM my_table
GROUP BY post_id, language;
The result of this query will be all the languages associated with the max index.
post_id | language | MAX(index)
1 | en | 3
1 | fr | 2
Hope this helps!
SQL GROUP BY clause is used to group rows according to distinct values of column specified into GROUP BY clause
According to description as mentioned above as a solution to it please try executing following SQL SELECT QUERY
SELECT post_id,language, max(index)
FROM my_table group by language

SQL query to find duplicate rows and return both IDs

I have a table of customers:
id | name | email
--------------------------
1 | Rob | spam#email.com
2 | Jim | spam#email.com
3 | Dave | ham#email.com
4 | Fred | eggs#email.com
5 | Ben | ham#email.com
6 | Tom | ham#email.com
I'm trying to write an SQL query that returns all the rows with duplicate email addresses but... I'd like the query result to return the original ID and the duplicate ID. (The original ID is the first occurrence of the duplicate email.)
The desired result:
original_id | duplicate_id | email
-------------------------------------------
1 | 2 | spam#email.com
3 | 5 | ham#email.com
3 | 6 | ham#email.com
My research so far has indicated it might involve some kind of self join, but I'm stuck on the actual implementation. Can anyone help?
We could handle this using a join, but I might actually go for an option which generates a CSV list of id corresponding to duplicates:
SELECT
email,
GROUP_CONCAT(id ORDER BY id) AS duplicate_ids
FROM yourTable
GROUP BY email
HAVING COUNT(*) > 1
Functionally speaking, this gives you the same information you wanted in your question, but in what is a much simplified form in my opinion. Because we order the id values when concatenating, the original id will always appear first, on the left side of the CSV list. Also, if you have many duplicates your requested output could become verbose and harder to read.
Output:
Demo
select
orig.original_id,
t.id as duplicate_id,
orig.email
from t
inner join (select min(id) as original_id, email
from t
group by email
having count(*)>1) orig on orig.email = t.email
having t.id!=orig.original_id
By the subquery we can find all ids for emails with duplicates.
Then we join the subquery by email and for each one use minimal id as original
UPDATE: http://rextester.com/BLIHK20984 cloned #Tim Biegeleisen's answer

select statement from 2 unrelated tables

There's 2 unconnected tables, with no common IDs
+---------------------+
| names |
+------+--------------+
| name | lastN |
+-------------+-------+
| Anthony | monta |
| Ryo | shizu |
+------+--------------+
+----------------------+
| nicknames |
+------+---------------+
| nickname |
+------+---------------+
| miso_hungry |
+------+---------------+
I'm trying to run a select query on both tables and currently doing something like:
SELECT names.name, nicknames.nickname
FROM names, nicknames
WHERE names.name="miso_hungry" OR nicknames.nickname="miso_hungry"
I'm getting back a weird results with repeating identical rows, which doesn't make sense.
For example if I search for miso_hungry with the query above it will return every row of "names" table for some reason and append the correct rows from the "nicknames" table..
Attaching a screenshot of the results
Above should show "NULL" under name column, since "miso_hungry" is not found in that column and I'm not sure why it prints every row of the "names" table also.
You can use UNION Clause
Each SELECT statement within UNION must have the same number of columns
The columns must also have similar data types
The columns in each SELECT statement must also be in the same order
So we need to made them satisfy above condition. We can use Aliasing to do this.
SELECT name,(SELECT NULL) as nickname FROM names WHERE name = "miso_hungry"
UNION
SELECT (SELECT NULL) as name, nickname FROM nicknames WHERE nickname = "miso_hungry"
Edited
If you want to get the match count from both table use query like below :
SELECT SUM(count) as count FROM (
SELECT count(*) as count FROM names WHERE name = "miso_hungry"
UNION ALL
SELECT count(*) as count FROM nicknames WHERE nickname = "miso_hungry"
) both_table
The order of execution in your statement is from,where,select. With and implicit join you get a cartesian product which given
use sandbox;
create table n(name varchar(20));
create table nn(nickname varchar(20));
insert into n values('Antony'),('Ryo');
insert into nn values('miso');
results in
MariaDB [sandbox]> SELECT n.name, nn.nickname
-> FROM n, nn;
+--------+----------+
| name | nickname |
+--------+----------+
| Antony | miso |
| Ryo | miso |
+--------+----------+
2 rows in set (0.00 sec)
The where clause is then applied - which yields the same result.

MySQL accessing two tables in single query

Here's a simplified version of my troubles. 3 tables, the first (transit) will be used in upcoming procedures and functions, the second (products) will hold stationary data about products, the third (userWatchList) will hold user-specific data related to products.
TABLE: transit
+---------+------+
| ranking | data |
+---------+------+
| | |
+---------+------+
TABLE: products
+----+------+-----------------+
| ID | data | importantnumber |
+----+------+-----------------+
| 1 | c | 10 |
| 2 | u | 20 |
| 3 | t | 20 |
| 4 | u | 40 |
+----+------+-----------------+
TABLE: userWatchList
+---------+----+
| ranking | ID |
+---------+----+
| 1 | 2 |
| 2 | 1 |
| 3 | 4 |
| 4 | 3 |
+---------+----+
I need to insert into "transit" the data and ranking of rows that are within the needed ranking range and the data of which meets certain requirements.
I now want the ranking and data of a product, that has an importantnumber value of 20.
Say the allowed ranking range was between 1 and 2, SELECT * FROM transit at the end of the desired process would output:
+---------+------+
| ranking | data |
+---------+------+
| 1 | 'u' |
+---------+------+
Say the allowed ranking range was between 1 and 3, SELECT * FROM transit at the end of the desired process would output:
+---------+------+
| ranking | data |
+---------+------+
| 1 | 'u' |
| 4 | 't' |
+---------+------+
My vision of a possible solution...
To make sure the ranking falls within the needed range, I thought I might use dynamic SQL:
SET #IDsRetrieveStmt = CONCAT("SELECT group_concat(ID SEPARATOR ',') INTO #IDsStr FROM userWatchList WHERE ranking BETWEEN ', #rankingmin,' AND ', #rankingmax,';');
PREPARE stmt FROM #IDsRetrieveStmt;
EXECUTE stmt;
Now. To add ranking value to those fields... what should i do? I imagine one option is somewhere along the lines of:
SET #fetch_data_stmt = CONCAT('INSERT INTO transit (data, ranking) SELECT data, ( **** ) FROM products WHERE ID IN ( ', #IDsStr, ') AND importantnumber=20;');
PREPARE stmt FROM #fetch_data_stmt;
EXECUTE stmt;
** some unknown magic here that fetches ranking from a row with the same ID from 'products' table. This could be SELECT ranking FROM userWatchList WHERE ID=ID, but as you see, the ID part will probably create a conflict. Also, it seems a bit ineffective to run a new SELECT query with every inserted row.
I am sure there is a more effective way of doing this that I haven't heard of yet.
What's the best way of achieving this? Thanks in advance!
The first, and most important, part of the answer is the query that generates the data you want. You need to join the two tables together and use your criteria as conditions in the query:
select ranking, data
from userWatchList u
join product p on p.ID = u.ID
where ranking between ? and ?
and importantnumber = ?
Of course substituting ? with your criteria.
The next part of the answer is more advice. Unless there's an extremely compelling reason to do so, don't create a table to hold the data output from this query, because it's derived data that is out of date the instant it's created, unless you put in complicated database infrastructure (triggers) to keep it fresh.
Instead, create a view, that's like a table to a client (an application), but is actually a query under the hood:
create view transit as
select ranking, data, importantnumber
from userWatchList u
join product p on p.ID = u.ID
Then to use:
select ranking, data
from transit
where ranking between ? and ?
and importantnumber = ?

filter sql rows using update statement

I am trying to write a query which can get invalid refby(is related to id), please check following db structure...
| id | acnumber | refby |
+----+-----------+--------+
| 1 | ac01 | 2 |
+----+-----------+--------+
| 2 | ac02 | 1 |
+----+-----------+--------+
| 3 | ac03 | 5 |
+----+-----------+--------+
As you can find there is no id with value of 5 in above table so query must return 3rd row as result.
I have tried...
SELECT * FROM tbl.members WHERE refby != (SELECT id FROM tbl.members WHERE id = refby)
But this is not giving correct results, please help, thanks.
SELECT * FROM members WHERE refby not in (SELECT id FROM members)
This should solve your problem
You can try this using not in:-
SELECT * FROM tbl.members WHERE refby not in (SELECT id FROM members)
This should be a LEFT JOIN, NOT IN is slow on large tables... assuming id and refid is an PRIMARY KEY or UNIQUE key (read unique within your dataset) then this query should return the same results.
SELECT
*
FROM
members members1
LEFT JOIN
members members2
ON members1.id = members2.refby
WHERE members2.id IS NULL
check the sqlfriddle http://sqlfiddle.com/#!2/05731/1