I have three tables, which are below with related details
1. Primary words
+----+---------+
| id | word |
+----+---------+
| 1 | Machine |
+----+---------+
| 2 | phone |
+----+---------+
2. Alternative words
+----+------------+-----------+
| id | primary_id | word |
+----+------------+-----------+
| 1 | 1 | system |
+----+------------+-----------+
| 2 | 1 | feature |
+----+------------+-----------+
| 3 | 2 | telephone |
+----+------------+-----------+
3. product table
+----+------------------+
| id | name |
+----+------------------+
| 1 | mobile system |
+----+------------------+
| 2 | computer machine |
+----+------------------+
| 3 | wired telephone |
+----+------------------+
Now twist is that whenever user search with "machine" in product table then display results of product with table name have "machine" or "system" or "feature" and if search with "system" or "feature" then also display results of "machine" or "system" or "feature".or vice vera.
Would you please suggest me how can solve this one?
If I understand correctly what you are asking...
SQL
SELECT *
FROM Product P
WHERE EXISTS(SELECT fw.Word --(6) select all products that exist in this query
FROM (SELECT pw.Word --(1) select all Primary words matching input
FROM PrimaryWords pw
WHERE pw.Word = 'machine'
UNION --(3) union the results from both selects
SELECT aw.Word --(2) select all Alternative words that match input or have its primary matching it
FROM PrimaryWords pw INNER JOIN AlternativeWords aw
ON pw.Id = aw.PrimaryId
WHERE pw.Word = 'machine'
OR aw.Word = 'machine') as fw --(4) alias the result
WHERE p.Name LIKE '%' || fw.Word || '%'); -- (5) filter products that match the valid words
you can read the comments ordered by the numbering in ().
First of all you need to take those two table data inti one and then give condition.
Select word from(
SELECT id,word FROM PrimaryWords
Union all
Select primaryid,word from AlternativeWords a
) where id in ( select id from primarywords where word='yoursearchketword'
Union
Select primaryid from AlternativeWords where word='yoursearchketword')
Updated Answer as per your product table.
Now you need to cross join the product table, because there is no relationship between them.
One more thing is you have to use like operator here to compare you desired result with prouct table's name column. Here I have given small idea How to accomplish that, but you can easily improve the same.
Select a.word,b.name from
(Select word from(
SELECT id,word FROM PrimaryWords
Union all
Select primaryid,word from AlternativeWords a
) where id in ( select id from primarywords where word='yoursearchketword'
Union
Select primaryid from AlternativeWords where word='yoursearchketword')) a, product b
Where a.word LIKE CONCAT('%',name, '%');
I suggest to put it in one table like this:
id | primary_id | word
1 | 1 | machine
2 | 2 | phone
3 | 1 | system
4 | 1 | feature
5 | 2 | telephone
And the query will be like this, lets take machine word as an example:
Select word from MyTable where primary_id in
(select primary_id from MyTable where word = "machine")
The output: machine, system, feature
Update
In case you have more columns, for example to show how much related are these words, you can add one more table to map between words, like the following:
id | w1_id | w2_id | relation
1 | 1 | 3 | 80
2 | 2 | 5 | 90
3 | 1 | 4 | 60
4 | 3 | 4 | 60
And the words table will list only the words like:
id| word
1 | machine
2 | phone
3 | system
4 | feature
5 | telephone
In this thread you will find a long discussion talking about way you have to design the layout of your schema as well as hints in how retrieve your data.
Related
We are looking to return rows of a query as groups and displaying all entries of the group in the sort order. Randomly based on the set_id... and then in order by the sort_id.
So, randomly it will show:
Carl,
Phil,
Wendy,
Tina,
Rick,
Joe
or
Tina,
Rick,
Joe,
Carl,
Phil,
Wendy
This query is always showing Tina/Rick/Joe first
SELECT * FROM products ORDER BY set_id, rand()
Any help would be appreciated
+---------+--------+-------+----------+
| id | set_id | name | sort_id |
+---------+--------+-------+----------+
| 1 | AA |Rick | 2 |
| 2 | BB |Carl | 1 |
| 3 | AA |Joe | 3 |
| 4 | AA |Tina | 1 |
| 5 | BB |Phil | 2 |
| 6 | BB |Wendy | 3 |
+---------+--------+-------+----------+
if you need a random comma separated name list this will do the trick.
This will keep the groups and the correct sorting within the group.
Query
SELECT
GROUP_CONCAT(Table_names_rand.names) as names
FROM (
SELECT
*
FROM (
SELECT
GROUP_CONCAT(name ORDER BY sort_id) as names
FROM
Table1
GROUP BY
set_id
)
AS Table1_names
ORDER BY
RAND()
)
AS Table_names_rand
Result
| names |
|-------------------------------|
| Carl,Phil,Wendy,Tina,Rick,Joe |
or
| names |
|-------------------------------|
| Tina,Rick,Joe,Carl,Phil,Wendy |
demo http://www.sqlfiddle.com/#!9/487ac9/9
if you need random names as records output.
Query
SELECT
Table1.name
FROM
Table1
CROSS JOIN (
SELECT
GROUP_CONCAT(Table_names_rand.names) as names
FROM (
SELECT
*
FROM (
SELECT
GROUP_CONCAT(name ORDER BY sort_id) as names
FROM
Table1
GROUP BY
set_id
)
AS Table1_names
ORDER BY
RAND()
)
AS Table_names_rand
)
AS Table_names_rand
ORDER BY
FIND_IN_SET(name, Table_names_rand.names)
Result
| name |
|-------|
| Carl |
| Phil |
| Wendy |
| Tina |
| Rick |
| Joe |
or
| name |
|-------|
| Tina |
| Rick |
| Joe |
| Carl |
| Phil |
| Wendy |
demo http://www.sqlfiddle.com/#!9/487ac9/28
If we strip away the randomness of the gorup ordering, your query would look like this:
SELECT
*
FROM
products
ORDER BY
set_id,
sort_id;
The ordering by set_id is necessary to "group" the results, without really grouping them. You do not want to group them, because then the rows with the same group would be aggregated, meaning that only one row per group would be put out.
Since you only want to randomize the groups, you need to write another query that assigns a random number to each group, like the one below:
SELECT
set_id,
RAND() as 'rnd'
FROM
products
GROUP BY
set_id
The GROUP BY clause makes sure, that each group is only selected once. The resultset will look like this:
| set_id | priority |
+--------+---------+
| AA | 0.21 |
| BB | 0.1 |
With that result we can then randomize the output, by combining both queries with a JOIN on the set_id field. This will add the randomly generated number from the second query to the result set of the first query and therefore extend the static set_id with the randomized, but still for all group members equal, rnd:
SELECT
products.*
FROM
products
JOIN (
SELECT
set_id,
RAND() as 'rnd'
FROM
products
GROUP BY
set_id
) as rnd ON rnd.set_id = products.set_id
ORDER BY
rnd.rnd,
products.set_id,
products.sort_id;
Keep in mind, that it is important to still group on products.set_id, because it may be possible that two groups get the same random number assigned. If the result would not be ordered by products.set_id those groups members would then be merged.
I have 3 tables
1)question
2)options
3)answers
Table 1 contains list of multiple choice questions. Table 2 contains multiple choices for the question.
Consider an example, I have the question "Which game do you like most?"
It is stored in table 1 with id 1. For this question there are 3 choices in table 2; as "cricket", "football", "tennis" with ids 1,2 & 3.
When a user answer this question the question id, and option id are stored to the third table as if he select football, entry in table 3 is question id and option id.
If another user select same option new entry is stored in table 3.
My need is that I want to get the count of each options in table 3.
Consider 10 users select cricket, 15 users select football, no user select tennis, so I need count as 10,15,0 with its corresponding option id
Table name: questions
--------------------------------
| id | question |
--------------------------------
| 1 | which game u like most |
Table Name: options
------------------------------------------------
| id | qid | option_name |
------------------------------------------------
| 1 | 1 | cricket |
------------------------------------------------
| 2 | 1 | football |
------------------------------------------------
| 3 | 1 | tennis |
------------------------------------------------
Table Name: answers
--------------------------------------------
| id | qid | optionId |
--------------------------------------------
| 1 | 1 | 3 |
---------------------------------------------
| 2 | 1 | 3 |
----------------------------------------------
| 3 | 1 | 2 |
----------------------------------------------
The above table means that, 2 people choose tennis, 1 people choose football and no one choose cricket. So I need result table as
------------------------------------------------------
| id | question | option_name | count |
------------------------------------------------------
| 1 | which game u like most | cricket | 0 |
-------------------------------------------------------
| 2 | which game u like most | football | 1 |
-------------------------------------------------------
| 3 | which game u like most | tennis | 2 |
-------------------------------------------------------
But when I tried, I didnt get the count for cricket, because no one selected cricket.I must get count of cricket as 0. Can any one help me solve the issue?
My sql code is
SELECT count(an.optionId) count , op.option_name, q.question from
questions q, options op, answers an where q.id=1
and q.id=op.qid
and op.id=an.optionId
group by q.question, op.option_name
Just use a LEFT JOIN and COALESCE() :
SELECT COALESCE(count(an.optionId),0) as count , op.option_name, q.question from
questions q
INNER JOIN options op
ON(q.id=op.qid )
LEFT OUTER JOIN answers an
ON(p.id=an.optionId)
where q.id=1
group by q.question, op.option_name
Please avoid the use of implicit join syntax(comma separated ) and use only the correct syntax of a join! LEFT JOIN with implicit syntax getting more hard to read and more easy to make mistakjes.
I have two tables messages and users I want to find out which users received the messages however the query is only returning one message.
My Schemas are as follow
Messages
msg_id | msg_content | recipients |
-----------------------------------
1 | Hello world | 1,2,3,4,5
2 | Test | 1,3,5
3 | Welcome | 1,2,4
Users
uid | fname | lname |
---------------------------
1 | John |Doe |
2 | Jane |Doe |
3 | Mark |Someone |
4 | Mary |lady |
5 | Anthony |Doe |
So I would love to see my results simply as
msg_id | msg_content | recipients |
-----------------------------------
1 | Hello world | John,Jane,Mark,Mary,Anthony
2 | Test | John,Mark,Anthony
3 | Welcome | John,Jane,Mary
So I am doing my query as so
SELECT msg_id,msg_content,fname AS recepients FROM messages a
LEFT JOIN users ON uid IN(a.recipients)
When I run that query I only get one recipient. Please advice. Thanks.
I think you have to use a alternative way for create tables
Messages
msg_id | msg_content |
----------------------
1 | Hello world |
2 | Test |
3 | Welcome |
Users
uid | fname | lname |
---------------------------
1 | John |Doe |
2 | Jane |Doe |
3 | Mark |Someone |
4 | Mary |lady |
5 | Anthony |Doe |
users_has_messages
uhm_id | uid | msg_id |
---------------------------
1 | 1 | 1 |
2 | 2 | 1 |
3 | 3 | 1 |
4 | 2 | 2 |
5 | 1 | 3 |
Then you can use your code
Okay, so this schema isn't the best (using comma separated lists of IDs is not a great idea, and the performance of any joins will get pretty bad pretty quick). Best bet is to have a third table mapping uid's to msg_id's as mentioned by #Thilina.
That said, this query will do probably what you're after:
SELECT msg_id,msg_content,GROUP_CONCAT(fname) AS recepients FROM messages a
LEFT JOIN users ON FIND_IN_SET(uid, a.recipients)
GROUP BY msg_id
I tried this in Oracle 12c and it is working fine.
So basically what I did is
- Separate the userid from recipient field and used this a columns.
- Join with USERS table to get user fnames
- Used LISTAGG function to aggregate it back.
For MySql we need to find the corresponding functions to Separate the IDs between commas, Convert it to rows and Aggregate. But the inherent logic would be same.
with users (user_id,fname) as (
select 1 ,'John' from dual union
select 2 ,'Jane' from dual union
select 3 ,'Mark' from dual union
select 4 ,'Mary' from dual union
select 5 ,'Anthony' from dual
),
messages(msg_id, msg_content,recipients) as(
select 1,'Hello world','1,2,3,4,5' from dual union
select 2 , 'Test' ,'1,3,5' from dual union
select 3,' Welcome','1,2,4' from dual
),
flat as(
select msg_id,msg_content,
REGEXP_SUBSTR (recipients, '[^,]+', 1, COLUMN_VALUE) as user_id
from messages,
TABLE(
CAST(
MULTISET(
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT(recipients ,',' ) + 1
) AS SYS.ODCINUMBERLIST
)
)
),
unames as
( select f.msg_id,f.msg_content,u.fname from flat f inner join users u
on f.user_id = u.user_id
order by f.msg_id
)
SELECT msg_id,msg_content,LISTAGG(fname, ',') WITHIN GROUP (ORDER BY fname) as recipients
from unames
group by msg_id,msg_content
I have 3 tables (called results,users,games), the first one get data from the others, all with a primary auto-increment ID like this:
results
id | idusers | idgames
--------+---------+----------
| |
| |
| |
users
id | name |
--------+---------+
1 | todd |
2 | mario |
3 | luigi |
games
id | play |
--------+---------+
1 | game1 |
2 | game2 |
3 | game3 |
I wish to randomize users ID and games ID, and join together inside the results table like this:
results
id | idusers | idgames
--------+---------+----------
1 | 3 | 2
2 | 1 | 1
3 | 2 | 1 (also duplicates are ok)
I know for randomize the ID it's supposed to use SELECT * FROM users ORDER BY NEWID() and for join together different tables I need to use INNER JOIN,
but how to make it all working together in one single query?
One method in MySQL is to use rand() and a correlated subquery:
select idusers,
(select id from games order by rand() limit 1) as idgames
from users;
If you have a reasonable amount of data (more than a few thousand rows), then there are more efficient methods.
I'm doing a search in my database. At the same time I have a tagging system installed. Not everything is tagged yet, so I also need the 'old fashioned' result from my database. Let me make it clear:
Table A
+----+----------------------------+
| ID | description |
+----+----------------------------+
| 0 | horse going bad |
| 1 | Older Years of Resolutions |
| 2 | The pirate |
| 3 | The Wish list |
| 4 | list that's no list |
+----+----------------------------+
table TAGS
+----+------------+
| ID | tag |
+----+------------+
| 0 | list |
| 1 | knockknock |
+----+------------+
table TAGLINKS
+-------+--------+
| TAGID | JOKEID |
+-------+--------+
| 0 | 2 |
| 0 | 3 |
+-------+--------+
When I do this search:
select * from A where locate('list',description)
I'll get ID 3 and 4 from table A, which is great.
When I do this search:
select * from tags
join taglinks on tagid=tags.id
join A on A.id=jokeid
where tag='list'
I get ID 2 and 3 from table A.
What I want to get back is ID 2, 3 and 4. So a join of the two results. I tried this:
select * from tags
join taglinks on tagid=tags.id
join A on A.id=jokeid or locate('list',description)
It seems to give me the right result, but it's so slow it clogs up the server (in reality the tables are MUCH bigger than the examples here). The reason I want a combined query is that I need functions like ORDER BY and LIMIT. So I'm looking to get the combined result from the above two queries.
As Eugen Rieck and Raheel Shan pointed out the answer is a union:
select A.* from tags
join taglinks on tagid=tags.id
join A on A.id=jokeid
where tag="list"
UNION
select * from A where locate("list",description)
Gives me ID 2, 3 and 4