How to efficiently do this mysql query? (Join and Check existence) - php

I'm doing a mysql query where I want to join two tables (Projects and Country):
+----------------+ +-------------------------+
| Project ID | | id |
+----------------+ +-------------------------+
| Name | | name |
+----------------+ +-------------------------+
| countryid | | lat, long (two columns) |
+----------------+ +-------------------------+
I'm using this query:
SELECT country.name, country.latitude, country.longitude
FROM projects JOIN country ON projects.countryid = country.id;
Fine until now! What happens next is each project has a class (TO-DO, DOING, DONE). Each class is one table that has all the project ids that belong to each.
The structure has to be like this, because each class has additional information. And that information only exists if the project belongs to that class. For example, a project in the TO-DO class has a "start_prevision_date" which is only valid in the TO-DO table (fields from other classes are still null).
What I want to do is assign one color to each project depending if he is TO-DO, DOING or DONE. A perfect solution would be a query that return values like:
ResultSet:
+--------------+------------------+-------------------+-------+-------+------+
| country.name | country.latitude | country.longitude | TO-DO | DOING | DONE |
+--------------+------------------+-------------------+-------+-------+------+
| Portugal | 13,4 | 15,6 | 1 | 0 | 0 |
+--------------+------------------+-------------------+-------+-------+------+
Can be in more than one column at once
Where 1 means he belongs and 0 means he doesn't belong. I could just check, and print the right color!
Is there a way to do this efficiently? Avoiding the use of extra three queries.
I hope I was clear.

You can use a query like this:
SELECT
country.name,
country.latitude,
country.longitude,
IF (TO-DO.sureField IS NULL, 0, 1) as TO-DO,
IF (DOING.sureField IS NULL, 0, 1) as DOING,
IF (DONE.sureField IS NULL, 0, 1) as DONE
FROM projects
LEFT JOIN country ON projects.countryid = country.id
LEFT JOIN TO-DO ON projects.projectid = TO-DO.projectid
LEFT JOIN DOING ON projects.projectid = DOING.projectid
LEFT JOIN DONE ON projects.projectid = DONE.projectid;
sureField has to be a field in each class table that is surely populated and not null when a project is in that class and null otherwise.
I tested in MySql reproducing a similar case and it worked.
This will give you:
ResultSet:
+--------------+------------------+-------------------+-------+-------+------+
| country.name | country.latitude | country.longitude | TO-DO | DOING | DONE |
+--------------+------------------+-------------------+-------+-------+------+
| Portugal | 13,4 | 15,6 | 1 | 1 | 0 |
+--------------+------------------+-------------------+-------+-------+------+
If the projects in DOING remain also in TO-DO, or:
ResultSet:
+--------------+------------------+-------------------+-------+-------+------+
| country.name | country.latitude | country.longitude | TO-DO | DOING | DONE |
+--------------+------------------+-------------------+-------+-------+------+
| Portugal | 13,4 | 15,6 | 0 | 1 | 0 |
+--------------+------------------+-------------------+-------+-------+------+
If the projects in DOING are deleted from TO-DO.
Therefore you will have to manage the query on result set appropriately.
Regards

Related

Summation Query with join codeigniter

I have 2 tables. One with a list of clients and another with a list of data. I am trying to create a table in my view that lists the client name along with the sum of a column(job_total) in the data table. I am able to write a query that works fine in most situations. The problem is, if I have not yet created a record in the data table I need to still display the client name with a balance of zero on my table in my view. Need some direction on how to handle this. I was thinking I need to query my list of clients and loop through that query just not sure how to do it.
I want my view to look like below:
+-------------+---------+
| Client Name | Balance |
+-------------+---------+
| xxx | $75.00 |
| xxx | $100.00 |
| xxx | $0.00 |
+-------------+---------+
Here is a rough layout of the two tables in my database:
cj_clients
+----+-------------+
| id | client name |
+----+-------------+
| 1 | client1 |
| 2 | client2 |
| x | xxx |
+----+-------------+
cj_data
+----+-----------+-----------+
| id | client_id | job_total |
+----+-----------+-----------+
| 1 | 1 | 5.00 |
| 2 | 1 | 10.00 |
| 3 | 1 | 15.00 |
+----+-----------+-----------+
The below code returns the desired results except when no entries have yet been made to the cj_data table. Not sure how to still get the client in the table view with a balance of $0.
$this->db->select('client_name,client_id, sum(job_total) AS balance')
->from('cj_data')
->join('cj_clients','cj_data.client_id = cj_clients.id')
->group_by('client_name');
return $this->db->get()->result();
You need to give left join
$this->db->select('client_name,client_id, IFNULL(sum(job_total),0) AS balance')
->from('cj_data')
->join('cj_clients','cj_data.client_id = cj_clients.id',"left") // here
->group_by('client_name');
return $this->db->get()->result();
I wrote IFNULL condition if record not found or it will show all data for all clients in cj_clients
Note: the Default behaviour of CodeIgniter is it will add inner join
if join not specified

Get all the data from a table and sometimes relate that table with another

My question is: Can I do this?
I try many things, some of them are:
Search 1
Search 2
Search 3
Search 4
I need all the info form Table A, and sometimes I need to join this table with Table B. My problem is that when I join both tables, if an specific parameter in Table A is not in Table B just give me the records when the specific parameter are, but I want all the records.
Table A
|--------------------------------|
| Table A |
|-------------------|------------|
| id_table_A | name | id_table_B |
|------------|------|------------|
| 1 | Joe | 1 |
|------------|------|------------|
| 2 | Ben | |
|------------|------|------------|
| 3 | Lya | |
|------------|------|------------|
| 4 | luis | 2 |
|------------|------|------------|
Table B
|----------------------|
| Table B |
|----------------------|
| id_table_B | Elements|
|------------|---------|
| 1 | Car |
|------------|---------|
| 2 | Byke |
|------------|---------|
| 3 | Moto |
|------------|---------|
What I want to show in my View is this:
|------------|------|------------|
| id_table_A | name | Elements |
|------------|------|------------|
| 1 | Joe | Car |
|------------|------|------------|
| 2 | Ben | |
|------------|------|------------|
| 3 | Lya | |
|------------|------|------------|
| 4 | luis | Byke |
|------------|------|------------|
My model
In my model this is what I tried:
"SELECT * FROM table_A, table_B where table_A.id_table_B = table_B.id_table_B"
But this query only show me data 1 and 4.
This can be done or not?
Thanks in advance.
You can use left join
SELECT *
FROM table_A
left join table_B on table_A.id_table_B = table_B.id_table_B
Left join is used when the keys between tables may not always match. In this case, the left join retrieves the correct match where it's possible and the values become NULL when not possible.
SQL LEFT JOIN Documentation
You need an explicit LEFT JOIN as opposed to the implicit INNER JOIN that is used when you simply list the tables like that. https://www.w3schools.com/sql/sql_join_left.asp

Mysql join query multiple values

I have a table called facility.
Structure looks as follows:
id | name
---------
1 | Hotel
2 | Hospital
3 | medical shop
I have an other table which is taking data from the above table and keeping multiple values in one column. View looks like below:
id | facilities
---------------
1 | Hospital~~medical shop~~Hotel
2 | Hospital~~Hotel
3 | medical shop~~Hotel
If I want to join these two tables how does the query look like?
I tried this, but it didn't work:
select overview.facilities as facility
from overview join facility on facility.id=overview.facilities;
you can do this with a bit of hackery
select o.facilities as facility
from overview o
join facility f on find_in_set(f.facilities, replace(o.facilities, '~~', ','));
I would highly recommend you change the way you are storing data. currently it is considered un normalized and that quickly becomes a monster to deal with
you should change your table structure to look something more like this
+----------+--------------+
| facility |
+----------+--------------+
| id | name |
+----------+--------------+
| 1 | Hotel |
| 2 | Hospital |
| 3 | medical shop |
+----------+--------------+
+-----------+-------------+
| overview |
+-----------+-------------+
| id | facility_id |
+-----------+-------------+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 3 |
| 7 | 1 |
+-----------+-------------+
Code Explanation:
basically you are wanting to find the matching facilities in the overview. one handy function MySQL has is FIND_IN_SET() that allows you to find an item in a comma separated string aka find_in_set(25, '11,23,25,26) would return true and that matching row would be returned... you are separating your facilities with the delimiter ~~ which wont work with find_in_set... so I used REPLACE() to change the ~~ to a comma and then used that in the JOIN condition. you can go from here in multiple ways.. for instance lets say you want the facility id's for the overview.. you just add in the select GROUP_CONCAT(f.id) and you have all of the id's... note if you do that you need to add a GROUP BY at the end of your query to tell it how you want the results grouped

MySQL database table setup

I want to know if i'm setting up this database schema the most effective way.
I am trying to output snowboarding terminology and what it means for each product on my website. The tables that I have set up for this:
products
+----+--------+--------+-------------+
| id | brand | name | product_type|
+----+--------+--------+-------------+
|8000| burton | diode | binding |
+----+--------+--------+-------------+
terminology_products
+----+--------+------------+
| id | att_id | product_id |
+----+--------+------------+
| 1 | 51 | 8000 |
| 2 | 52 | 8000 |
+----+--------+------------+
terminology
+--------+-----------+--------+---------------------+
| att_id | type | key | value |
+--------+-----------+--------+---------------------+
| 52 | baseplate | EST | details about EST |
| 53 | baseplate | Hinge | details about Hinge |
+--------+-----------+--------+---------------------+
Then I query
SELECT products.ProductName, products.Brand, terminology.type, terminology.key, terminology.value
FROM products
JOIN terminology_products
ON terminology_products.product_id = products.ID
JOIN terminology
ON terminology_products.att_id = terminology.att_id
WHERE products.id = 8000
And get the results that I am looking for, but I feel like this could be an over complicated way of doing it. Is there a simpler way? Thank you for any insight or help.
Yes, it is a correct way. It's common solution.
Your "terminology_products" table contains foreign keys, and it allows you to make "many to many" relation between tables.

Search and print result from multiple queries

I have 3 tables. service_robot_errorservice contains a foreign key that is primary key in the service table. robot_error does not have ore give any foreign keys.
TABLE: robot_error
| id | date | stop_time | start_time | robot | position | error_code | observation | solution | downtime | logged_by |
| 1 | 02.01.2012 | 14:51:31 | 14:52:00 | 20 | 25/7 | 214 | X dir | Rob off | 29 | XXX |
TABLE: service
| service_id | service_date | service_module | service_user | service_comment | service_type |
| 1 | 13.01.2012 | Robot | XXX | Test service | Errorservice |
TABLE: service_robot_errorservice
| errorservice_id | service_id | errorservice_robot | errorservice_tracksensor | errorservice_gripper |
| 5 | 1 | 54 | OK | OK |
When a user searches for a robot (eks 16) i want the result to be every instance in robot_error orderd by date AND if the robot has service registered this instance should also be shown in the result.
Example:
robot_error instance
robot_error instance
robot_error instance
service instance
robot_error instance
service instance
That is what i want the result to look. I've tryed to first get the result out of this SQL
SELECT * FROM robot_error WHERE robot = '16'
and make a while to loop all results in robot_error table but i cant figure out how i can make the result from service tables to be shown, can anyone help? :)
I'd recommend using joins.
JOIN: Return rows when there is at least one match in both tables
LEFT JOIN: Return all rows from the left table, even if there are no matches in the right table
RIGHT JOIN: Return all rows from the right table, even if there are no matches in the left table
FULL JOIN: Return rows when there is a match in one of the tables
But you'd have to have a relation between robot_error and service before you can use a query with a join. It would probably look something like this:
SELECT * FROM robot_error LEFT OUTER JOIN services ON robot_error.ID = services.FK WHERE robot = '16'
Jeff Atwood demonstrates the different Joins quite nicley

Categories