mysql help to make a report - mysql

i have two table here is table_user and table_feedback like below
table_user
| id | name |
|----|------|
| 1 | john |
| 2 | tony |
| 3 | mona |
table_feedback
| id | rate | user_id | date |
|----|------|---------|----------|
| 1 | 1 | 3 |2015-11-2 |
| 2 | 1 | 2 |2015-11-2 |
| 3 | 1 | 3 |2015-11-1 |
I wanted to show report by date from table_feedback including name and id from table_user and all user will be show if table_feedback didn't contain the user id then this will be return blank data. I have idea about inner join and here is my query. problem is that the query return 2 row only but i need 3 row including table_user id 1 with blank column rate.
Here is my query below.
SELECT
table_user.id,
table_user.name,
table_feedback.rate,
table_feedback.date
FROM table_feedback
INNER JOIN table_user
ON table_user.id = table_feedback.user_id
WHERE table_feedback = '2015-11-2'
expected_result_table
| user_id | name | rate | date |
|-------- |------|------|----------|
| 1 |jony | |2015-11-2 |
| 2 |tony | 1 |2015-11-2 |
| 3 |mona | 1 |2015-11-2 |

The solution to this is an outer join. Anytime you find yourself thinking along the lines of "I need to see all rows from this table, regardless of a match in another table..." you should look to an outer join.
We can use an outer join to select all users, and link them to the feedbackTable in our JOIN clause. This will return null values for any columns in the table that don't match up. Try this:
SELECT u.id, u.name, t.rate, t.dateCol
FROM userTable u
LEFT JOIN feedbackTable t ON t.user_id = u.id AND t.dateCol = '2015-11-02';
Here is an SQL Fiddle example. As a side note, it is good practice not to name date columns date since that is a keyword in MySQL.
Edit based on your expected results:
To make sure the date column appears in each row, you can hardcode it into your select. If you choose to use a variable, you won't have to update the date twice each time, you can just update the declaration:
SET #reportDate = '2015-11-02';
SELECT u.id, u.name, t.rate, #reportDate
FROM userTable u
LEFT JOIN feedbackTable t ON t.user_id = u.id AND t.dateCol = #reportDate;
Here is an updated SQL Fiddle.

My guess is you need to use LEFT JOIN:
SELECT
table_user.id,
table_user.name,
table_feedback.rate,
table_feedback.date
FROM table_user
LEFT JOIN table_feedback
ON table_user.id = table_feedback.user_id
AND table_feedback.date = '2015-11-2'

In order to include user without feedback you need to use LEFT OUTER JOIN
SELECT
table_user.id,
table_user.name,
table_feedback.rate,
'2015-11-2'
FROM table_feedback
LEFT OUTER JOIN table_user
ON table_user.id = table_feedback.user_id
WHERE table_feedback = '2015-11-2'

You need to do three modifications to your query:
Use LEFT JOIN instead of INNER JOIN (to get all the users)
Change the table order (first the table you want to get all rows
from)
As the user 1 (john) does not have any data in the second table, you
cannot limit the rows in WHERE-clause. Do the limitation in JOIN
instead, so it applies only to the rows that are matching the JOIN.
So:
SELECT
table_user.id,
table_user.name,
table_feedback.rate,
table_feedback.date
FROM table_user
LEFT JOIN table_feedback ON table_user.id = table_feedback.user_id and table_feedback.date = '2015-11-02'

Related

Multiple join with filtering

I have 3 table
Group
idG
Student
idS
idG
Rating
idR
idS
date
relations is 1(Group)-∞(Student),1(Student)-∞(Rating)
I need to select all Group where idG is 1(for example) JOIN Student with LEFT JOIN Rating where date is between.
I trying to use query such this
SELECT *
FROM `group` AS g
JOIN student
ON g.idG = 1
LEFT JOIN rating
ON rating.idR = student.idS
WHERE rating.`date` between '2019-02-01' and '2019-02-28';
Result set of this query is student of group which have rating between date,but as I know left join must fetch NULL rating to the student which have no rating between the date. I expecting this result
idG | idS | idR | date|
1 | 1 | NULL | NULL|
1 | 2 | 1 | 2019-02-08|
1 | 3 | 3 | 2019-02-10|
1 | 4 | NULL | NULL|
You could put the criteria for the left joined table in the JOIN.
SELECT grp.idG, stu.idS, rat.idR, rat.`date`
FROM student stu
JOIN `group` AS grp
ON grp.idG = stu.idG
LEFT JOIN rating rat
ON rat.idS = stu.idS
AND rat.`date` BETWEEN '2019-02-01' AND '2019-02-28'
WHERE stu.idG = 1;
The thing with LEFT JOIN is that when you also want those that don't match, that a criteria in the WHERE clause for the joined table will discard all non-matches.
Since it then discards the NULL's on the right side of the join.

SQL left join: how to return the newest from tableB and grouped by another field

I've been trying for two days, without luck.
I have the following simplified tables in my database:
customers:
| id | name |
| 1 | andrea |
| 2 | marco |
| 3 | giovanni |
access:
| id | name_id | date |
| 1 | 1 | 5000 |
| 2 | 1 | 4000 |
| 3 | 2 | 1500 |
| 4 | 2 | 3000 |
| 5 | 2 | 1000 |
| 6 | 3 | 6000 |
| 7 | 3 | 2000 |
I want to return all the names with their last access date.
At first I tried simply with
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id
But I got 7 rows instead of 3 as expected. So I understood I need to use GROUP BY statemet as the following:
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id GROUP BY customers.id
As far I know, GROUP BY combines using a random row. In fact I got unordered access dates with several tests.
Instead I need to group every customer id with its corresponding latest access! How this can be done?
You have to get the latest date from the access table with a group by on the the name_id, then join this result with the customer table. Here is the query:
select c.id, c.name, a.last_access_date from customers c left join
(select id, name_id, max(access_date) last_access_date from access group by name_id) a
on c.id=a.name_id;
Here is a DEMO on sqlfiddle.
I think this is what you'd like to achieve:
SELECT c.id, c.name, max(a.date) last_access
FROM customers c
LEFT JOIN access a ON c.id = a.name_id
GROUP BY c.id, c.name
The LEFT join will return all entries in table customers regardless if the join criteria (c.id = a.name_id) is satisfied. This means that you might get some NULL entries.
Example:
Simply add a new row in the customers table (id: 4, name: manuela). The output will have 4 rows and the newest row will be (id: 4, last_access: null)
I would do this using a correlated subquery in the ON clause:
SELECT a.*, c.*
FROM customers c LEFT JOIN
access a
ON c.id = a.name_id AND
a.DATE = (SELECT MAX(a2.date) FROM access a2 WHERE a2.name_id = a.name_id);
If this statement is true:
I need to group every customer id with its corresponding latest access! How this can be done?
Then you can simply do:
select a.name_id, max(a2.date)
from access a
group by a.name_id;
You do not need the customers table because:
All customers are in access, so the left join is not necessary.
You need no columns from customers.

Incorrect results from three table join

I have these three tables in my database:
tblCustomer (id,name,address)
tblLoan (id,customerId,LoanAmount,date)
tblPayment (id,customerId,ReceivedAmount,date)
I want to find the total loanAmount for a customer and how much they have paid.
I wrote this query:
SELECT c.fname, SUM(l.amount), SUM(p.amount)
FROM tblCustomer c
JOIN tblLoan l ON (l.customerId = c.id)
JOIN tblPayment p ON (p.customerId = c.id)
WHERE c.id = 3;
It returns results but they are incorrect.
First, as others have mentioned, your syntax is likely incorrect because you do not have matching column names, but you said you had incorrect results, so I would assume that's not your problem as you were able to run your query..
The problem that I think you are most likely having is that by joining the two tables together like that, rows appear twice for each customer. Am I correct in assuming that your 'incorrect' results are double what you would expect? Let me illustrate for those who don't understand. Consider this data set, with shortened column values:
tblCustomer:
| id | name |
+----+------+
| 1 | Adam |
| 2 | John |
| 3 | Jane |
tblLoan, and for simplicity we'll say the payment table looks the same:
| customerID | loanAmount |
+------------+------------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 3 | 300 |
| 2 | 200 |
If I perform the following query (without summing values, just getting the values I want:
SELECT c.id, c.name, l.loanAmount, p.receivedAmount
FROM tblCustomer c
JOIN tblLoan l ON l.customerid = c.id
JOIN tblPayment p ON p.customerid = c.id
WHERE c.id = 3;
It returns this result set:
| id | name | loanAmount | receivedAmount |
+----+------+------------+----------------+
| 3 | Jane | 100 | 100 |
| 3 | Jane | 100 | 300 |
| 3 | Jane | 300 | 100 |
| 3 | Jane | 300 | 300 |
So notice that because we're joining two tables based on a relationship to a third table, were actually creating a cartesian product which is causing the problem. So, what I recommend you do is use subqueries for these two tables. One subquery will pull the loan values, one the payment values, and you can join those together on the id value.
It will look like this:
SELECT t.id, t.totalLoan, w.totalReceived
FROM(SELECT c.id, SUM(l.loanAmount) AS totalLoan
FROM tblCustomer c
JOIN tblLoan l ON l.customerid = c.id
WHERE c.id = 3) t
JOIN(SELECT c.id, SUM(p.receivedAmount) AS totalReceived
FROM tblCustomer c
JOIN tblPayment p ON p.customerid = c.id
WHERE c.id = 3) w
ON t.id = w.id;
And this should give you the values you want. Here is what I tested on SQL Fiddle.
FYI, YOUR COLUMN NAMES ARE WRONG!!!
There is no such column named fname in table tblCustomer
There is no such column named amount in table tblLoan
There is no such column named amount in table tblPayment
You won't get the right result if you don't have the appropriate column names. Even when using aliases, your column name should be EXACTLY THE SAME as in your database table. That's because, you are aliasing TABLES in JOIN queries, not COLUMNS.
So, re-write your query in the following way:
SELECT c.name, SUM(l.LoanAmount), SUM(p.ReceivedAmount)
FROM tblCustomer c
JOIN tblLoan l ON l.customerId = c.id
JOIN tblPayment p ON p.customerId = c.id
WHERE c.id = 3
Note that there's no need to get brackets around the ON clause in JOIN.

How to select 2 table with condition and show all data

How to select 2 table with condition and show all data
store_profile
id + store_name
1 | Accessorize.me
2 | Active IT
3 | Edushop
4 | Gift2Kids
5 | Heavyarm
6 | Bamboo
store_fee
id + store_id + date_end
1 | 1 | 27-6-2013
2 | 2 | 29-8-2013
3 | 3 | 02-6-2013
4 | 4 | 20-4-2013
Below is my previous query
$query = "select sp.id, sp.store_name, sf.id, sf.store_id, sf.date_end from store_profile sp, store_fee sf where sf.store_id=sp.id"
and the result is something like this :
1 | Accessorize.me 27-6-2013
2 | Active IT 29-8-2013
3 | Edushop 02-6-2013
4 | Gift2Kids 20-4-2013
but what i want is show all store name including date_end but if no date_end still can show store name with empty date_end
You want to use an outer join. With an outer join, columns on the joining table do not need to match the conditional columns in the joined table to get results:
SELECT * FROM store_profile sp LEFT JOIN store_fee sf ON (sf.store_id = sp.id)
Use a left join:
select sp.id, sp.store_name, sf.id, sf.store_id, sf.date_end
from store_profile sp left join store_fee sf on sf.store_id=sp.id
The syntax you are using is interpreted as INNER JOIN, which means that stores without a corresponding entry in store_profile won't show up. You want to use LEFT JOIN:
SELECT sp.id, sp.store_name, sf.id, sf.store_id, sf.date_end
FROM store_profile sp
LEFT JOIN store_fee sf
ON sf.store_id=sp.id
LEFT JOIN means that all records in the first table will be returned, even if there's not a match in the second table.

joining 4 tables

I am new to this kind of relational type of database design. I just designed the database in this manner. However, I am quite confused on this JOIN of MySQL. What should be my query to join all this table. If you can see the table users is the reference of all the tables.
users
+----------+----------------+-----------------+
| users_id | users_level_id | users_status_id |
+----------+----------------+-----------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
+----------+----------------+-----------------+
users_credentials
+----------+---------------------------+-----------------------------+----------------------------+
| users_id | users_credential_username | users_credential_email | users_credential_password |
+----------+---------------------------+-----------------------------+----------------------------+
| 1 | super | super#gmail.com | $5$e94e9e$vptscyHjm8rdX0j6 |
| 2 | admin | admin#gmail.com | $5$fVuOmySyC0PttbiMn8in0k7 |
+----------+---------------------------+-----------------------------+----------------------------+
users_level
+----------------+-------------------------+
| users_level_id | users_level_description |
+----------------+-------------------------+
| 1 | Super Administrator |
| 2 | Administrator |
+----------------+-------------------------+
users_status
+-----------------+--------------------------+
| users_status_id | users_status_description |
+-----------------+--------------------------+
| 0 | Disabled |
| 1 | Enabled |
+-----------------+--------------------------+
Try this
SELECT u.*, uc.*, ul.*, us.*
FROM users u
INNER JOIN users_credentials uc
ON u.users_id = uc.users_id
INNER JOIN users_level ul
ON u.users_level_id = ul.users_level_id
INNER JOIN users_status us
ON u.users_status_id = us.users_status_id
Note the use INNER JOIN: this means that if a user does not have corresponing record on joined table it won't be shown; if you need to return every user even without matching record on related tables, change INNER JOIN with LEFT JOIN.
EDITED after user comment:
If you want to return just some column, define it as this example
SELECT uc.users_credential_username AS username,
uc.users_credential_email AS email,
uc.users_credential_password AS pwd,
ul.users_level_description AS level,
us.users_status_description AS status
This is a simple query that will join all of them
select *
from users
left join users_credentials
on users_credentials.users_id = users.users_id
left join users_level
on users_level.users_level_id = users.users_level_id
left join users_status
on users_status.users_status_id = users.users_status_id
EDIT
if you want to fetch data from different tables
user this
select users.* , users_credentials.* , users_level.* , users_status.*
from users
left join users_credentials
on users_credentials.users_id = users.users_id
left join users_level
on users_level.users_level_id = users.users_level_id
left join users_status
on users_status.users_status_id = users.users_status_id
I think this look like this :
SELECT * FROM users
LEFT JOIN user_credentials ON users.user_id = user_credential.user_id
LEFT JOIN user_level ON users.users_level_id = users_level.users_level_id
and so on..
Use this type of query....
SELECT c.*, l.*, s.*
FROM users AS u
INNER JOIN users_credentials AS c ON (u.users_id = C.users_id)
INNER JOIN users_level AS l ON (u.users_level_id= l.users_level_id)
INNER JOIN users_status AS s ON (u.users_status_id= s.users_status_id)
Where you can specify the field what you want in .* ...
Join is used to fetch data from normalized tables which have foreign key relation with the reference table.
For the above table with join you can fetch data among two tables with the help of reference table.
For example
Select * from users a JOIN users_credentials b
ON a.user_id=b.user_id JOIN users_level c
ON c.users_level_id=a.users_level_id
where users_credential_username='super';
The result of this query will give you the detail like users_level_description for the user with users_credential_username=super.