Suppose I have a table like:
ID|Word |Reference
1 |Dog |1
1 |Fish |2
1 |Sheep|3
2 |Dog |4
2 |Fish |5
3 |Sheep|6
4 |Dog |7
I want to select all ID's that have the word Dog AND Sheep. So the result should be ID's: 1 and 2. I tried using this query:
SELECT ID FROM `Table` WHERE Word='Dog' OR Word='Fish' GROUP BY ID Having Word='Dog AND Word='Fish'
However, this AND in the Having clause makes me get 0 results. So, am I doing something wrong or is there another way to achieve wat I want based on MySQL query only (to optimize speed, since it has to search through many rows with the same setup as in the example above)
Basically the problem is the AND statement over multiple rows with the same ID.
UPDATE:
I need to get the reference for the ID's that where found. E.g. when the ID 1 and 2 are returned I need to know that ID 1 has reference 1 and 2. ID 2 has reference 3 and 4. Currently, I'm using this query:
SELECT ID FROM `Test` WHERE Word in ('Dog', 'Fish') GROUP BY ID HAVING count(DISTINCT Word) = 2;
Thanks
Here are two solutions that return the correct records, the first as individual records by ID and Reference, and the second with one record per ID and the Words and References as comma separated in columns.
Setup table and populate rows:
DROP TABLE IF EXISTS `list1`;
CREATE table `list1` (
id int(10),
Word varchar(10),
Reference int(10)
);
INSERT INTO `list1` (`ID`, `Word`, `Reference`)
VALUES
(1, 'Dog',1),
(1 ,'Fish',2),
(1 ,'Sheep',3),
(2 ,'Dog',4),
(2 ,'Sheep',5),
(3 ,'Sheep',6),
(4 ,'Dog',7);
Returns one row for each combination of ID and Word
SELECT
t.`ID`,
t.`Word`,
t.`Reference`
FROM `list1` as t
JOIN (
SELECT
t1.`ID` as `ref_id`
FROM `list1` AS t1
WHERE `Word` in ('Sheep','Dog')
GROUP BY t1.`ID`
HAVING count(DISTINCT t1.`Word`) = 2
) AS ts
ON t.`ID` = ts.`ref_id`
WHERE t.`Word` in ('Sheep','Dog')
ORDER BY t.`ID`,t.`Word`;
Results
ID | Word | Reference
1 | Dog | 1
1 | Sheep | 3
2 | Dog | 4
2 | Sheep | 5
Returns one row per ID, with a comma separated list of Words in one column, and a comma separated list of Reference in another.
SELECT
t.`ID`,
GROUP_CONCAT(t.`Word`) AS `Words`,
GROUP_CONCAT(t.`Reference`) AS `References`
FROM `list1` as t
JOIN (
SELECT
t1.`ID` as `ref_id`
FROM `list1` AS t1
WHERE `Word` in ('Sheep','Dog')
GROUP BY t1.`ID`
HAVING count(DISTINCT t1.`Word`) = 2
) AS ts
ON t.`ID` = ts.`ref_id`
WHERE t.`Word` in ('Sheep','Dog')
GROUP BY t.`ID`
ORDER BY t.`ID`,t.`Word`;
Results:
ID | Words | References
1 | Dog,Sheep | 1,3
2 | Dog,Sheep | 4,5
You need to join the table on itself. This way you can pick up where the id's are the same for instances where dog and sheep overlap.
Try this:
declare #t table (id int , Word varchar(10) )
insert into #t (ID, Word) values (1, 'Dog'),
(1 ,'Fish'),
(1 ,'Sheep'),
(2 ,'Dog'),
(2 ,'Sheep'),
(3 ,'Sheep'),
(4 ,'Dog')
select t.ID
from #t as t
join #t as t1 on t1.id = t.id
where t.word = 'Dog' and t1.word = 'Sheep'
Here's one way to do it by joining your table to itself.
SELECT t1.id FROM `Table` t1
INNER JOIN `Table` t2 ON t1.id = t2.id
WHERE t1.word='Dog' AND t2.word='Sheep';
The answer for my problem is solved using underneath query:
SELECT ID, GROUP_CONCAT(Reference) as ReferencesGrouped FROM `Test` WHERE Word in ('Dog', 'Fish') GROUP BY ID HAVING count(DISTINCT Word) = 2;
This will return me:
ID|ReferencesGrouped
1 |1,4
2 |4,5
Related
I am working on a photography project and I am facing a bit issue with joining tables and retrieving data from mysql database.
I have created two tables for this project. One table named cm_team is for team members and another table named cm_events for photography events..Assume to shoot a event, we require 6 persons and the id of the person is stored in cm_events table.
As you can see from the above images.. I am storing the id's of members of cm_team in cm_events table.. I wish to obtain the name of the team member in the respective highlighted fields in the cm_events table..Any help is highly appreciated.
for example my desired output should be: instead of 5 under team_lead heading, I should get the name corresponding to 5 i.e Arjun
Something like this? (cleaner and faster than subqueries)
SELECT
`event`.client_name,
`event`.client_number,
# some more event cols ..
`team_lead`.`cm_name` AS `team_lead`,
`candid_photo`.`cm_name` AS `candid_photo`,
`candid_video`.`cm_name` AS `candid_video`,
`traditional_photo`.`cm_name` AS `traditional_photo`,
`traditional_video`.`cm_name` AS `traditional_video`,
`helper`.`cm_name` AS `helper`
FROM cm_events `event`
JOIN cm_team `team_lead` ON `team_lead`.`cm_code` = `event`.`team_lead`
JOIN cm_team `candid_photo` ON `candid_photo`.`cm_code` = `event`.`candid_photo`
JOIN cm_team `candid_video` ON `candid_video`.`cm_code` = `event`.`candid_video`
JOIN cm_team `traditional_photo` ON `traditional_photo`.`cm_code` = `event`.`traditional_photo`
JOIN cm_team `traditional_video` ON `traditional_video`.`cm_code` = `event`.`traditional_video`
JOIN cm_team `helper` ON `helper`.`cm_code` = `event`.`helper`
With Sub queries
DROP TABLE IF EXISTS T,t1;
CREATE TABLE T (
id int, name varchar(10));
insert into t values
(1 , 'aaa'),
(2 , 'bbb');
create table t1 (
id int, team_lead int,team_a int);
insert into t1 values
(1,1,2),
(2,2,2);
select t1.id, (select t.name from t where t.id = t1.team_lead) team_lead,
(select t.name from t where t.id = t1.team_a) team_a
from t1;
+------+-----------+--------+
| id | team_lead | team_a |
+------+-----------+--------+
| 1 | aaa | bbb |
| 2 | bbb | bbb |
+------+-----------+--------+
2 rows in set (0.001 sec)
I have a products table which contains all my products. Those products table gets filled permanently with new products. However, I want to have the possibility to "hold up"/"pin" certain products to a place in the returned query collection.
Means, I want to set something like rank_index which contains the number the product should have in the returned query collection.
Example:
id title rank_index
1 An awesome product
2 Another product 5
3 Baby car
4 Green carpet 2
5 Toy
Lets assume the default order would be the id. But because the rank_index is set for the product with the id 4 I would like to get the collection with the following order of ids returned: 1, 4, 3, 5, 2.
Is this somehow possible to do? The rank_index column was just an idea of mine. I mean.. I also could do this on the php side and do a normal query which does only include the products without an rank_index and one which only contains products with an index_rank and order them manually on the php side.
However, because this takes a lot of time and processing power I am looking for a solution which is done by the database... Any ideas?
Btw: I am using Laravel 8 if this makes any difference.
Kind regards
This is a very tricky problem. If you try the other approach setting consecutive values -- like 2 and 3 -- you will see that they do not work.
There may be simpler ways to solve this. But, here is a brute force approach.
It constructs a derived table by enumerating the rows in the original table.
It adds into this table (using a left join) all the force-ranked values.
It joins in the rest of the values by enumerating the empty slots both in table1 and in the derived table.
So:
with recursive n as (
select row_number() over (order by id) as n
from table1 t1
),
nid as (
select n.n, t1.id
from n left join
table1 t1
on t1.rank_index = n.n
),
nids as (
select n.n, coalesce(n.id, t1.id) as id
from (select nid.*, sum(nid.id is null) over (order by nid.n) as seqnum
from nid
) n left join
(select t1.*, row_number() over (order by id) as seqnum
from table1 t1
where rank_index is null
) t1
on n.seqnum = t1.seqnum
)
select t1.*
from nids join
table1 t1
on t1.id = nids.id
order by nids.n;
Use the rank_index if it's not null as the ordering, id otherwise:
Since you want the rank_index to be ahead of an id, a -0.5 adjustment is made:
SELECT *
FROM table
ORDER BY IF(rank_index IS NULL, id, rank_index - 0.5)
You can use IF clause and to have the correct the number to get te right order, so
CREATE TABLE table1 (
`id` INTEGER,
`title` VARCHAR(18),
`rank_index` INT
);
INSERT INTO table1
(`id`, `title`, `rank_index`)
VALUES
('1', 'An awesome product', NULL),
('2', 'Another product', '5'),
('3', 'Baby car', NULL),
('4', 'Green carpet', '2'),
('5', 'Toy', NULL);
SELECT *
FROM table1
ORDER BY IF(rank_index IS NULL, id, rank_index + .01)
+----+--------------------+------------+
| id | title | rank_index |
+----+--------------------+------------+
| 1 | An awesome product | NULL |
| 4 | Green carpet | 2 |
| 3 | Baby car | NULL |
| 5 | Toy | NULL |
| 2 | Another product | 5 |
+----+--------------------+------------+
db<>fiddle here
I have two table (table 1 and table 2) . I want to show all the rows from table1 by joining with table2 which have multiple rows with same table1 id (foreign key relation) and will sort the result by table2 priority column (order by desc).
Table1
Table2
Result will be
thanks in advance
Edit
Table1
id name
1 test1
2 test2
3 test5
4 test7
5 test9
6 test3
Table2
id table1_id event priority
1 2 abc 0
2 2 kbc 0
3 2 abc 2
4 2 kbc 1
5 4 fgg 2
6 4 dss 3
7 1 fgfg 2
8 5 fgfg 2
9 6 xcxc 1
10 6 fgfh 3
Result
id_table1 name event priority
4 test7 dss 3
6 test3 fgfh 3
2 test2 abc 2
1 test1 fgfg 2
5 test9 fgfg 2
3 test5 NULL NULL
In the question you mentioned you need to select the data where id from table1 is available more than once in the table2 which does not match with the result set you gave.
Considering the original requirement the following should do the trick
select
t2.table1_id as id_table1,
t1.name,
t2.priority,
t2.event
from table1 t1
join
(
select
p1.table1_id,
p1.event,
p2.priority
from table2 p1
join(
select
max(priority) as priority,
table1_id
from table2
group by table1_id having count(*) > 1
)p2
on p2.table1_id = p1.table1_id and p2.priority = p1.priority
)t2
on t1.id = t2.t1id
order by t2.priority desc
Here is a demo
The result will get the same event corresponding the max priority column
This will get the result set that you want. You mentioned that you only need the items table1 ids that reflects more than once but result query shows tableid1 "1" even though it is only present once:
SELECT DISTINCT t1.id,t1.name ,t2.event, t2.priority
FROM TABLE2 t2
right join
TABLE1 t1
on t1.id=t2.table1_id
order by t2.priority desc
Try this query:
SELECT t1.*,t2.priority FROM table1 t1
INNER JOIN table2 t2
ON t1.id=t2.id
ORDER BY t2.priority DESC
Primary key and foreign key should have the same name. The syntax should be
SELECT Table1.id_table1,Table1.name,Table2.event,Table.priority FROM
Table1 LEFT JOIN Table2 ON
Table1.id=Table2.id
ORDER BY Table2.priority DESC
Make the following changes in Table2:
Get rid of the first column or rename it
Rename second column(your foreign key) to "id".
This question already has an answer here:
Select all rows that have at least a list of features
(1 answer)
Closed 9 years ago.
I have a mysql table
Table A
--------------------
item_id category_id
--------------------
1 1
1 2
1 4
2 1
2 3
Would like to make an sql query that will select all matches in an array
example:
given category_ids are 1,4 it should return only item_id 1
given category_ids are 1 it should return item_id 1 and 2
Thanks
For categories 1, 4:
SELECT item_id, COUNT(*) c
FROM TableA
WHERE category_id IN (1, 4)
GROUP BY item_id
HAVING c = 2
For category 1:
SELECT item_id, COUNT(*) c
FROM TableA
WHERE category_id IN (1)
GROUP BY item_id
HAVING c = 1
I think you should be able to see the pattern -- the HAVING clause should match the number of categories.
This assumes that item_id, category_id is unique in the table.
given category_ids are 1,4
SELECT item_id, category_id
FROM TableA
WHERE category_id IN (1, 4)
given category_ids are 1
SELECT item_id, category_id
FROM TableA
WHERE category_id IN (1)
I have several tables with different numbers and types of columns, and a single column in common.
+--------+---------+------------+-------------+
| person | beardID | beardStyle | beardLength |
+--------+---------+------------+-------------+
+--------+-------------+----------------+
| person | moustacheID | moustacheStyle |
+--------+-------------+----------------+
I want to fetch all the results that match a given value of the shared column. I can do it using multiple select statements like this:
SELECT * FROM beards WHERE person = "bob"
and
SELECT * FROM moustaches WHERE person = "bob"
But this requires multiple mysql API calls, which seems inefficient. I was hoping I could use UNION ALL to get all the results in a single API call, but UNION requires that the tables have the same number and similar type of columns. I could write a SELECT statement that would manually pad the results from each table by adding columns with NULL values, but that would quickly get unmanageable for a few more tables with a few more columns.
I'm looking for a result set roughly like this:
+--------+---------+------------+-------------+-------------+----------------+
| person | beardID | beardStyle | beardLength | moustacheID | moustacheStyle |
+--------+---------+------------+-------------+-------------+----------------+
| bob | 1 | rasputin | 1 | | |
+--------+---------+------------+-------------+-------------+----------------+
| bob | 2 | samson | 12 | | |
+--------+---------+------------+-------------+-------------+----------------+
| bob | | | | 1 | fu manchu |
+--------+---------+------------+-------------+-------------+----------------+
Is there a way to achieve this that's fast and maintainable? Or am I better off running a separate query for each table?
Clarification:
I'm not looking for a cartesian product. I don't want a row for every combination of beard-and-moustache, I want a row for every beard and a row for every moustache.
So if there are 3 matching beards and 2 matching moustaches I should get 5 rows, not 6.
this should be working fine:
SELECT * FROM `beards` b LEFT OUTER JOIN `mustaches` ON (0) WHERE person = "bob"
UNION ALL
SELECT * FROM `beards` b RIGHT OUTER JOIN `mustaches` ON (0) WHERE person = "bob"
you don't have to handle the columns by yourself. the left and right outer join do this job.
unfortunately mysql doesn't have a full join. that's why you have to do it this way with a union
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b RIGHT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) RIGHT OUTER JOIN `day` ON (0)
this is a local test i made
Join on person....
I.e.
Select
t1.(asterix), t2.(asterix)
FROM
beards t1
INNER JOIN
moustaches t2 On t2.person = t1.person
SELECT *
FROM beards
JOIN moustaches
ON moustaches.person = beards.person
WHERE person = "bob"
I had fun with this, not sure it's entirely manageable with what more you have to add, but it accomplished the goal.
create table beard (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int )
create table moustache(
person varchar(20)
,moustacheID int
,moustacheStyle varchar(20))
insert into beard
select 'bob', 1, 'rasputin', 1
union select 'bob', 2, 'samson', 12
insert into moustache
select 'bob', 1, 'fu manchu'
declare #facialhair table (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int
,moustacheID int
,moustacheStyle varchar(20))
declare #i int
declare #name varchar(20)
set #name = 'bob'
set #i = (select COUNT(*) from beard where person = #name)
+ (select COUNT(*) from moustache where person = #name)
print #i
while #i > 0
begin
insert into #facialhair (person, beardID, beardStyle, beardLength)
select person, beardID, beardStyle, beardLength
from beard
where person = #name
set #i = #i-##ROWCOUNT
insert into #facialhair (person, moustacheID, moustacheStyle)
select person, moustacheID, moustacheStyle
from moustache
where person = #name
set #i = #i-##ROWCOUNT
end
select *
from #facialhair
I think you would be better by making queries for data in each table.
One of other possibilities is to concatenate data from all columns into one big string (you could choose some sign to separete column's values), then you should be able to use union all clause to combine results from each query - but then you will have to parse each row.. And data types will be lost.