Doubts about queries in MySQL - mysql

I wanted to make a query with Rails, something like this:
filters = Filter.joins(:category_filters).where("category_id IN (?)", params[:categories]).group("filters.id")
And the MySQL statement that is making is this:
SELECT `filters`.* FROM `filters` INNER JOIN `category_filters` ON `category_filters`.`filter_id` = `filters`.`id` WHERE (category_id IN ('9,4')) GROUP BY filters.id
At first sight, this query is ok, but when I look the results, its wrong. Let me explain you.
First, this is a query to the table filters:
select * from filters;
+----+----------+-------+----------+------------+------------+
| id | name | other | optional | created_at | updated_at |
+----+----------+-------+----------+------------+------------+
| 1 | material | 1 | 1 | NULL | NULL |
| 2 | abc | 1 | 0 | NULL | NULL |
| 3 | xyz | 0 | 0 | NULL | NULL |
| 4 | 123a | 0 | 0 | NULL | NULL |
+----+----------+-------+----------+------------+------------+
Second, this is a query to the table category_filters:
select * from category_filters;
+----+-----------+-------------+------------+------------+
| id | filter_id | category_id | created_at | updated_at |
+----+-----------+-------------+------------+------------+
| 1 | 1 | 1 | NULL | NULL |
| 2 | 2 | 1 | NULL | NULL |
| 3 | 1 | 9 | NULL | NULL |
| 4 | 2 | 9 | NULL | NULL |
| 5 | 1 | 4 | NULL | NULL |
| 6 | 3 | 4 | NULL | NULL |
+----+-----------+-------------+------------+------------+
And now, the query generated by Rails (the first query):
SELECT `filters`.* FROM `filters` INNER JOIN `category_filters` ON `category_filters`.`filter_id` = `filters`.`id` WHERE (category_id IN ('9,4')) GROUP BY filters.id;
+----+----------+-------+----------+------------+------------+
| id | name | other | optional | created_at | updated_at |
+----+----------+-------+----------+------------+------------+
| 1 | material | 1 | 1 | NULL | NULL |
| 2 | abc | 1 | 0 | NULL | NULL |
+----+----------+-------+----------+------------+------------+
Why is this happening?
But now, this is similar query, instead of using IN I used OR, like this:
SELECT `filters`.* FROM `filters` INNER JOIN `category_filters` ON `category_filters`.`filter_id` = `filters`.`id` WHERE (category_filters.category_id=9 or category_filters.category_id=4) GROUP BY filters.id;
+----+----------+-------+----------+------------+------------+
| id | name | other | optional | created_at | updated_at |
+----+----------+-------+----------+------------+------------+
| 1 | material | 1 | 1 | NULL | NULL |
| 2 | abc | 1 | 0 | NULL | NULL |
| 3 | xyz | 0 | 0 | NULL | NULL |
+----+----------+-------+----------+------------+------------+
What is happening?

In your WHERE clause, try this (assuming category_id is a number):
category_id IN (9,4)
else this (assuming category_id is a string)
category_id IN ('9','4')
instead of this (in your original query)
category_id IN ('9,4')

If you just do
where(:category_id => params[:categories]
Rails will create the proper SQL syntax for you

Related

MySQL Query into single multi table join

End goal is to get tbl_output.output based on intent and slot attributes. I am currently handling it programmatically and would like to combine into one query if possible. I am open to restructuring any of the tables if needed.
SELECT id as intent_id FROM `tbl_intent` WHERE `name` = 'Car'
SELECT id as slot_id1 FROM `tbl_slot` WHERE `name` = '2018'
SELECT id as slot_id2 FROM `tbl_slot` WHERE `name` = 'Chevrolet'
SELECT id as slot_id3 FROM `tbl_slot` WHERE `name` = 'Corvette'
Example should return tbl_output ID 1 field "output".
SELECT
*,
output_id
FROM xref_intent_slot
LEFT JOIN tbl_slot slot1 ON xref_intent_slot.slot_id=slot1.id AND slot1.name='2018'
LEFT JOIN tbl_slot slot2 ON xref_intent_slot.slot_id=slot2.id AND slot2.name='Chevrolet'
LEFT JOIN tbl_slot slot3 ON xref_intent_slot.slot_id=slot3.id AND slot3.name='Corvette'
WHERE `intent_id` = (SELECT id from tbl_intent WHERE `name` = 'Car')
+----+-----------+-----------+---------+------+------+------+-----------+------+----------+-----------+
| id | output_id | intent_id | slot_id | id | name | id | name | id | name | output_id |
+----+-----------+-----------+---------+------+------+------+-----------+------+----------+-----------+
| 1 | 1 | 1 | 1 | 1 | 2018 | NULL | NULL | NULL | NULL | 1 |
| 2 | 1 | 1 | 2 | NULL | NULL | 2 | Chevrolet | NULL | NULL | 1 |
| 3 | 1 | 1 | 3 | NULL | NULL | NULL | NULL | 3 | Corvette | 1 |
| 4 | 2 | 1 | 4 | NULL | NULL | NULL | NULL | NULL | NULL | 2 |
| 5 | 2 | 1 | 2 | NULL | NULL | 2 | Chevrolet | NULL | NULL | 2 |
| 6 | 2 | 1 | 5 | NULL | NULL | NULL | NULL | NULL | NULL | 2 |
+----+-----------+-----------+---------+------+------+------+-----------+------+----------+-----------+
This should only return ID 1, 2, and 3 which is output_id 1. Then use that value to get the output from tbl_output?
Tables:
+-------------------+
| tbl_intent |
| tbl_output |
| tbl_slot |
| xref_intent_slot |
+-------------------+
Table: tbl_intent
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | MUL | NULL | |
+-------+--------------+------+-----+---------+----------------+
+----+------+
| id | name |
+----+------+
| 1 | Car |
+----+------+
Table: tbl_slot
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | MUL | NULL | |
+-------+--------------+------+-----+---------+----------------+
+----+-----------+
| id | name |
+----+-----------+
| 1 | 2018 |
| 2 | Chevrolet |
| 3 | Corvette |
| 4 | 2017 |
| 5 | Camaro |
+----+-----------+
Table: tbl_output
+--------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| output | text | NO | | NULL | |
+--------+---------+------+-----+---------+----------------+
+----+----------------+
| id | output |
+----+----------------+
| 1 | Found Corvette |
| 2 | Found Camaro |
+----+----------------+
Table: xref_intent_slot
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| output_id | int(11) | NO | | NULL | |
| intent_id | int(11) | NO | | NULL | |
| slot_id | int(11) | NO | | NULL | |
+-----------+---------+------+-----+---------+----------------+
+----+-----------+-----------+---------+
| id | output_id | intent_id | slot_id |
+----+-----------+-----------+---------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 2 |
| 3 | 1 | 1 | 3 |
| 4 | 2 | 1 | 4 |
| 5 | 2 | 1 | 2 |
| 6 | 2 | 1 | 5 |
+----+-----------+-----------+---------+
SELECT
`output`
FROM tbl_output
WHERE
id =
(
SELECT
output_id
FROM xref_intent_slot
LEFT JOIN tbl_slot slot1 ON xref_intent_slot.slot_id=slot1.id AND slot1.name='2018'
LEFT JOIN tbl_slot slot2 ON xref_intent_slot.slot_id=slot2.id AND slot2.name='Chevrolet'
LEFT JOIN tbl_slot slot3 ON xref_intent_slot.slot_id=slot3.id AND slot3.name='Corvette'
WHERE `intent_id` = (SELECT id from tbl_intent WHERE `name` = 'Car') AND
(slot1.name = '2018' OR
slot2.name = 'Chevrolet' OR
slot3.name = 'Corvette')
group by output_id
HAVING (count(output_id) = 3)
)
LIMIT 0,1

Select Same Column From 2 Tables into 1 Column in View

I have two column with the same name in different tables.
I want to join them into one column in a view.
Here my first table stocks:
+----------+------------+------------+---------+----------------+--------+
| stock_id | stock_cost | stock_left | item_id | purchasedtl_id | trx_id |
+----------+------------+------------+---------+----------------+--------+
| 1 | 1000 | 0 | 1 | 1 | 1 |
| 2 | 1000 | 5 | 1 | 2 | 2 |
| 3 | 1000 | 1 | 1 | 3 | 4 |
+----------+------------+------------+---------+----------------+--------+
Second table stocks_out
+-------------+----------------+--------------+---------+----------+------------+--------+
| stockout_id | stockout_price | stockout_qty | item_id | stock_id | saledtl_id | trx_id |
+-------------+----------------+--------------+---------+----------+------------+--------+
| 1 | 2000 | 1 | 1 | 1 | 1 | 3 |
+-------------+----------------+--------------+---------+----------+------------+--------+
And I want to join them to be like this trx_id, trx_no, trx_closetime, trx_type stock_id, stock_cost, stockout_id, stock_out_cost, stockout_price, item_id
item_id is the field I want to join in one column.
The current Query is :
select `transactions`.`trx_id` AS `trx_id`,`transactions`.`trx_no` AS `trx_no`,`transactions`.`trx_closetime` AS `trx_closetime`,`transactions`.`trx_type` AS `trx_type`,`stocks`.`stock_id` AS `stock_id`,`stocks`.`stock_cost` AS `stock_cost`,`stock_out`.`stockout_id` AS `stockout_id`,`stock_out`.`stockout_price` AS `stockout_price` from ((`transactions` left join `stocks` on(`stocks`.`trx_id` = `transactions`.`trx_id`)) left join `stock_out` on(`stock_out`.`trx_id` = `transactions`.`trx_id`)) order by `transactions`.`trx_closetime`;
And the current result:
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
| trx_id | trx_no | trx_closetime | trx_type | stock_id | stock_cost | stockout_id | stockout_price |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
| 1 | 02002-02-170415-001 | 2017-04-15 19:40:03 | 2 | 1 | 1000 | NULL | NULL |
| 2 | 02002-02-170415-002 | 2017-04-15 19:40:13 | 2 | 2 | 1000 | NULL | NULL |
| 3 | 02002-01-170415-001 | 2017-04-15 19:40:57 | 1 | NULL | NULL | 1 | 2000 |
| 4 | 02002-02-170415-003 | 2017-04-15 19:41:14 | 2 | 3 | 1000 | NULL | NULL |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
Found it guys.
I just need to add the following query as the column
COALESCE(`stocks`.`item_id`, `stocks_out`.`item_id`) AS `item_id`
So the query will be like
select `transactions`.`trx_id` AS `trx_id`,`transactions`.`trx_no` AS `trx_no`,`transactions`.`trx_closetime` AS `trx_closetime`,`transactions`.`trx_type` AS `trx_type`,`stocks`.`stock_id` AS `stock_id`,`stocks`.`stock_cost` AS `stock_cost`,`stock_out`.`stockout_id` AS `stockout_id`,`stock_out`.`stockout_price` AS `stockout_price`, COALESCE(`stocks`.`item_id`, `stocks_out`.`item_id`) AS `item_id from ((`transactions` left join `stocks` on(`stocks`.`trx_id` = `transactions`.`trx_id`)) left join `stock_out` on(`stock_out`.`trx_id` = `transactions`.`trx_id`)) order by `transactions`.`trx_closetime`;
And the result:
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+
| trx_id | trx_no | trx_closetime | trx_type | stock_id | stock_cost | stockout_id | stockout_price | item_id |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+
| 1 | 02002-02-170415-001 | 2017-04-15 19:40:03 | 2 | 1 | 1000 | NULL | NULL | 1 |
| 2 | 02002-02-170415-002 | 2017-04-15 19:40:13 | 2 | 2 | 1000 | NULL | NULL | 1 |
| 3 | 02002-01-170415-001 | 2017-04-15 19:40:57 | 1 | NULL | NULL | 1 | 2000 | 1 |
| 4 | 02002-02-170415-003 | 2017-04-15 19:41:14 | 2 | 3 | 1000 | NULL | NULL | 1 |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+

Select and Update Tables in mysql in same mysql statement

I have mysql query :-
select id,CustomerName, Scenario,StepNo,InTransit,IsAlef,Min(RunNo) as run,IsDominant,IsActive from RequestInfo
group by CustomerName, Scenario,StepNo,InTransit,IsAlef,RunNo
having concat(CustomerName,Scenario,StepNo,InTransit,IsAlef,count(RunNo)) in
(select concat(CustomerName,Scenario,StepNo,InTransit,IsAlef,Min(Total)) from
(select CustomerName, Scenario,StepNo,InTransit,IsAlef,RunNo, count(RunNo) Total from RequestInfo
group by CustomerName, Scenario,StepNo,InTransit,IsAlef,RunNo
) b
group by CustomerName, Scenario,StepNo,InTransit,IsAlef);
which gives output :-
+------+--------------+--------------+--------+-----------+--------+------+------------+----------+
| id | CustomerName | Scenario | StepNo | InTransit | IsAlef | run | IsDominant | IsActive |
+------+--------------+--------------+--------+-----------+--------+------+------------+----------+
| 1 | Cleartrip | SearchFlight | 1 | No | No | 1 | NULL | NULL |
| 327 | HotStar | SearchTv | 1 | No | No | 1 | NULL | NULL |
| 836 | NDTV | LiveTv | 1 | No | No | 2 | NULL | NULL |
| 1090 | YATRA | SearchFlight | 1 | No | No | 2 | NULL | NULL |
+------+--------------+--------------+--------+-----------+--------+------+------------+----------+
i want to set the last two columns as "Yes" only for the rows which are been shown in the above result.

MySQL query not producing the correct result

Given this:
MariaDB [master]> describe history;
+--------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+----------------+
| historyid | int(10) unsigned | NO | PRI | NULL | auto_increment |
| amount | float | NO | | NULL | |
| subsidy | char(1) | NO | | NULL | |
| last_payment | date | NO | | NULL | |
| amount_paid | float | NO | | NULL | |
| balance | float | NO | | NULL | |
| attend | char(1) | NO | | N | |
| attend_date | date | NO | | NULL | |
| groupid | int(11) unsigned | NO | | NULL | |
| clientid | int(10) unsigned | NO | MUL | NULL | |
| memberid | int(10) unsigned | NO | MUL | NULL | |
+--------------+------------------+------+-----+---------+----------------+
MariaDB [master]> describe participation;
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| partid | int(11) | NO | PRI | NULL | auto_increment |
| notes | varchar(255) | NO | | NULL | |
| groupdate | date | NO | | NULL | |
| clientid | int(10) unsigned | NO | MUL | NULL | |
| memberid | int(10) unsigned | NO | MUL | NULL | |
+-----------+------------------+------+-----+---------+----------------+
MariaDB [master]> select * from participation;
+--------+-----------+------------+----------+----------+
| partid | notes | groupdate | clientid | memberid |
+--------+-----------+------------+----------+----------+
| 194 | test test | 2016-01-26 | 3 | 1 |
+--------+-----------+------------+----------+----------+
How do I write the following query
MariaDB [master]> SELECT attend_date, groupdate, h.clientid, h.memberid
-> FROM history AS h
-> LEFT JOIN participation AS p ON groupdate = attend_date
-> WHERE h.memberid = "1"
-> AND MONTH(attend_date) = "1"
-> AND YEAR(attend_date) = "2016"
-> AND attend_date <> "0000-00-00"
-> ORDER BY attend_date ASC;
+-------------+------------+----------+----------+
| attend_date | groupdate | clientid | memberid |
+-------------+------------+----------+----------+
| 2016-01-26 | 2016-01-26 | 3 | 1 |
| 2016-01-26 | 2016-01-26 | 4 | 1 |
| 2016-01-26 | 2016-01-26 | 1 | 1 |
| 2016-01-26 | 2016-01-26 | 2 | 1 |
| 2016-01-28 | NULL | 3 | 1 |
| 2016-01-28 | NULL | 4 | 1 |
| 2016-01-28 | NULL | 1 | 1 |
| 2016-01-28 | NULL | 2 | 1 |
+-------------+------------+----------+----------+
so that my return is like this
+-------------+------------+----------+----------+
| attend_date | groupdate | clientid | memberid |
+-------------+------------+----------+----------+
| 2016-01-26 | 2016-01-26 | 3 | 1 |
| 2016-01-26 | NULL | 4 | 1 |
| 2016-01-26 | NULL | 1 | 1 |
| 2016-01-26 | NULL | 2 | 1 |
| 2016-01-28 | NULL | 3 | 1 |
| 2016-01-28 | NULL | 4 | 1 |
| 2016-01-28 | NULL | 1 | 1 |
| 2016-01-28 | NULL | 2 | 1 |
+-------------+------------+----------+----------+
The whole idea here is to be able to pull all the clients who don't yet have notes applied to a groupdate matching history table's attend_date. And only when a note is posted will the groupdate be set to the attend_date. Yet, the query, as I have it , returns a groupdate of 2016-01-26 for all four clients instead of just the one. Thanks in advance for your help.
Looking at your table structure, I see the relationship between your attend_date and groupdate on which you attempted to join, but I also see that those tables have clientid and memberid in common.
Since you only joined on the date, you're getting back a cartesian product of all clients and all dates for that condition. You can make the join's ON clause more specific by adding additional conditions with a logical AND.
LEFT JOIN participation AS p
ON groupdate = attend_date
AND h.clientid = p.clientid
It isn't fully obvious to me if your desired result set also requires a condition to match memberid between those tables, but if it does, the solution is as simple as another AND.
LEFT JOIN participation AS p
ON groupdate = attend_date
AND h.memberid = p.memberid
It helps not to think of a join's ON clause as merely a match between common columns, though that's the most common way it is used. Instead the ON clause just needs to present some condition that when true results in a joined row being returned (just like you're used to with a WHERE clause). That means you can place just about anything there which can be evaluated as true or false, allowing for complex joining logic. And the same can be said for ORDER BY and GROUP BY.

Help with MySQL join 3 Tables

I am working on a registration system that uses the following tables:
Person (name etc.), Course (course date), Registration (association table pid, cid)
Person
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
Example Data:
select * from person;
+-----+--------+
| id | name |
+-----+--------+
| 101 | Graham |
| 102 | Lisa |
| 103 | John |
+-----+--------+
Course
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
| date | date | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
select * from course;
+----+---------+------------+
| id | name | date |
+----+---------+------------+
| 1 | Hip Hop | 2011-06-08 |
| 2 | Dancing | 2006-06-23 |
| 3 | Running | 2007-07-08 |
+----+---------+------------+
Registration
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| pid | int(11) | YES | | NULL | |
| cid | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
select * from registration;
+----+------+------+
| id | pid | cid |
+----+------+------+
| 1 | 101 | 1 |
| 2 | 101 | 2 |
| 3 | 103 | 2 |
+----+------+------+
I would like to find person(s) that have no registration records within the past two years. I am attempting to join the tables based on date calculation but it does not seem to work this way. Is this possible with mysql or is my approach of trying this with one query wrong?
query I have come up with:
select * from
(person left join registration on person.id = registration.pid)
left join course on course.id = registration.cid
AND DATE_FORMAT(`course`.`date`, "%m.%Y") > DATE_FORMAT( DATE_SUB(NOW(), INTERVAL 2 YEAR),"%m.%Y")
WHERE
registration.id IS NULL;
+-----+------+------+------+------+------+------+------+
| id | name | id | pid | cid | id | name | date |
+-----+------+------+------+------+------+------+------+
| 102 | Lisa | NULL | NULL | NULL | NULL | NULL | NULL |
+-----+------+------+------+------+------+------+------+
It should list person 102 and 103 since both registrations are older than 2 years and no other records of newer course dates can be found...
Give this a shot, using a NOT EXISTS clause:
select p.* from person p
where not exists (select 1 from person px
join registration rx on px.id = rx.pid
join course cx on rx.cid = cx.id
where px.id = p.id
and cx.date > DATE_SUB(NOW(), INTERVAL 2 YEAR))