MySQL query - Select a unique column comparing other column - mysql

I've a table with the following structure:
id | property_id | location_type
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 3 | 2
5 | 4 | 1
6 | 4 | 2
id - is the primary key of the table. property_id is the property ID of my database (foreign key). location_type is beach (value - 1), mountain (value - 2)
Can you please help me in getting the SQL query to select the property_id with location_type = 1 AND location_type = 2 i.e. a property has beach and mountains.
I have lot of filters (around 9 types of location_type and other filters). I'm creating a property search engine with filters. Please help in getting the most optimized query so load time is less.

select
property_id
from table
where location_type in (1,2)
group by property_id
having count(distinct(location_type)) = 2
if you don't have duplicates you can remove distinct clause.

A self-join would eliminate the need for a subquery, although that doesn't mean that it will be faster; normal profiling rules apply:
SELECT table1.property_id
FROM table table1
INNER JOIN table tabel2 ON table1.property_id = table2.property_id
WHERE table1.location_type = 1
AND table2.location_type = 2

Related

Mysql IN function

class_table
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1,2,3,4 |
+----+-------+--------------+
student_mark
+----+----------+--------+
| id |student_id| marks |
+----+----------+--------+
| 1 | 1 | 12 |
+----+----------+--------+
| 2 | 2 | 80 |
+----+----------+--------+
| 3 | 3 | 20 |
+----+----------+--------+
I have these two tables and i want to calculate the total marks of student and my sql is:
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN
(SELECT `student_id` FROM `class_table` WHERE `teac_id` = '1')
But this will return null, please help!!
DB fiddle
Firstly, you should never store comma separated data in your column. You should really normalize your data. So basically, you could have a many-to-many table mapping teacher_to_student, which will have teac_id and student_id columns.
In this particular case, you can utilize Find_in_set() function.
From your current query, it seems that you are trying to getting total marks for a teacher (summing up marks of all his/her students).
Try:
SELECT SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
In case, you want to get total marks per student, you would need to add a Group By. The query would look like:
SELECT sm.`student_id`,
SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
GROUP BY sm.`student_id`
Just in case you want to know why, The reason it returned null is because the subquery returned as '1,2,3,4' as a whole. What you need is to make it returned 1,2,3,4 separately.
What your query returned
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN ('1,2,3,4')
What you expect is
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN (1,2,3,4)
The best way is it normalize as #madhur said. In your case you need to make the teacher and student as one to many link
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1 |
+----+-------+--------------+
| 2 | 1 | 2 |
+----+-------+--------------+
| 3 | 1 | 3 |
+----+-------+--------------+
| 4 | 1 | 4 |
+----+-------+--------------+
If you want to filter your table based on a comma separated list with ID, my approach is to
append extra commas at the beginning and at the end of a list as well as at the beginning and at the end of an ID, eg.
1 becomes ,1, and list would become ,1,2,3,4,. The reason for that is to avoid ambigious matches like 1 matches 21 or 12 in a list.
Also, EXISTS is well-suited in that situation, which together with INSTR function should work:
SELECT SUM(`marks`)
FROM `student_mark` sm
WHERE EXISTS(SELECT 1 FROM `class_table`
WHERE `teac_id` = '1' AND
INSTR(CONCAT(',', student_id, ','), CONCAT(',', sm.student_id, ',')) > 0)
Demo
BUT you shouldn't store related IDs in one cell as comma separated list - it should be foreign key column to form proper relation. Joins would become trivial then.

Convert a column of set values to a column of individual values in MySQL

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;

Finding cooccuring values in MYSQL weak relation table

I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1

my sql order by with values

I have a list of products which have an ID column as primary key, and category id which is a foreign key.. I want to overthem by a certain way.
ID | Name | CategoryID
----------------------------
1 | one | 1
2 | two | 2
3 | three | 1
4 | four | 3
5 | five | 5
6 | six | 4
7 | seven | 2
8 | eight | 1
if the above is my table. I want to get them in an SQL like the folowing
if want order these products in a certain way where all the products of category 5 needs to be
appearing first I am running a query like this.
SELECT * FROM Product ORDER BY CategoryID IN (5), ID
this does the job well.
but now i am in need to show the category id 5 first and then category id 2 first and the rest
if I try
SELECT * FROM Product ORDER BY CategoryID IN (5), CategoryID IN (2), ID ASC
that doesnt work.
any suggestions
You can use MySQL's FIELD() function:
SELECT * FROM Product ORDER BY FIELD(CategoryID, 2, 5) DESC
See it on sqlfiddle.
Try this way:
SELECT * FROM Product
ORDER BY
case CategoryID
when 5 then 0
when 2 then 1
else 2
end
, ID

Get all records with an ID, but the first records should match another condition also

I am fetching all stations which belong to a station group from my database. SELECT * FROM stations WHERE station_group_id = 1.
Now, from all the fetched results, I want certain ones to appear first (e.g. the stations which have line_id = 2 to appear first). For example, if this is my stations table:
id | station_group_id | line_id
-------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
I would like the output to be:
id | station_group_id | line_id
-------------------------------
1 | 1 | 2
2 | 1 | 1
3 | 1 | 3
So that line_id = 2 is the first record in the output.
I thought about using ORDER BY, but it isn't quite an order issue, it is more a "preference" one.
So, is it possible to place some records on top of the output, based on a condition, preferably in one query? Thanks!
Try Below:
SELECT * FROM stations
WHERE station_group_id = 1
ORDER BY if(line_id in('2','X','Y','Z'),0,1)
SELECT * FROM stations WHERE station_group_id = 1 and line_id = 2
union
SELECT * FROM stations WHERE station_group_id = 1 and
line_id != 2 order by line_id asc
As you are saying, it is actually a preference, so you should either model it as an extra field on the table (e.g. ordinal, or order, or preferredOrder), or you keep sorting by line_id, and do the "special sort" in code. (find element with id=2, move to top)