SQL Query, LEFT JOINs with NULLs - mysql

I am trying to create a list of hours by Company and Date, even if the Company does not have data for that Date. I've tried LEFT JOINing a Calendar table and Grouping on its dates and then Company, but to no avail.
SELECT cal.date, comp.name, comp.hours
FROM company AS comp
LEFT JOIN calendar AS cal ON cal.date=comp.date
GROUP BY cal.date, comp.name
I expect to get NULL outputs when a Company does not have hours for that Date, like so:
2018-01-01 Company A 100
2018-01-01 Company B NULL
2018-01-02 Company A NULL
2018-01-02 Company B NULL
2018-01-03 Company A 100
2018-01-03 Company B 50
But it only returns rows where data can be found, as if I used an INNER JOIN. Any help would be appreciated. Thank you!

You've made 3 mistakes, of which 2 are relevant:
You swapped the order of the tables in the join (easy to fix)
Your query does not reflect your stated intent (harder to fix)
You are using GROUP BY when it is unnecessary (irrelevant in this particular case)
I'll focus on 2. You're close to your goal but not all there. Fixing the first and third mistakes, here is your query:
SELECT cal.date, comp.name, comp.hours
FROM calendar AS cal
LEFT JOIN company AS comp ON cal.date=comp.date
This is going to do something like the following:
For each cal_row in calendar:
For each comp_row in company:
If cal_row.date equals comp_row.date:
Create output row and append to output
If no rows created:
Create output row, fill with NULLs, and append to output
So your query guarantees at least 1 output row per date. What you are looking for is 1 output row per date, per company. One way to do this is by creating an intermediate table, and left-joining to that:
SELECT tbl.date, tbl.name, comp.hours
FROM (
SELECT DISTINCT cal.date, comp.name
FROM calendar AS cal
CROSS JOIN company as comp
) AS tbl
LEFT JOIN LEFT JOIN company AS comp ON
tbl.date = comp.date AND tbl.name= comp.name
http://sqlfiddle.com/#!9/517b4e/11/0
This matches your desired output.

you can use calendar as a left table,in that case you will get all the date
SELECT cal.date, comp.name, comp.hours
FROM calendar AS cal
LEFT JOIN company AS comp ON cal.date=comp.date
I have not found any aggregate function so i removed group by

Related

SQL Counting Calls from other tables

Having trouble counting from a separate table. I'm only getting how many callers are making calls rather than each individual count for every call.
I have went in and checked that most callers make multiple calls but i'm not sure how to show this.
I'm looking for which Company has >18 calls.
Tables are:
Customer
Company_ref
Company_name
Contact_id
Address_1
Address_2
Caller
Caller_id
Company_ref
First_name
Last_name
Issue
Call_ref
Caller_id
Call_date
Detail
Query:
SELECT Company_name, Count(Call_ref)
from Customer JOIN Issue on (Contact_id = Caller_id)
Group by Company_name
and example of the outcome is
Affright Retail 5
Askew Inc. 5
Askew Shipping 6
Bai Services 2
Cell Group 5
Comfiture Traders 5
which is only counting how many callers rather than how many calls made
This should work (MS SQL-Server):
select a.Company_ref, count(c.Call_ref) as Calls from caller a
join Issue b on (a.Caller_id = b.Caller_id)
join Customer c on (a.Company_ref = c.Company_ref)
group by a.Company_ref
adding a Where clause to determine companies with 18 or more calls:
select * from (
select a.Company_ref, count(c.Call_ref) as Calls from caller a
join Issue b on (a.Caller_id = b.Caller_id)
join Customer c on (a.Company_ref = c.Company_ref)
group by a.Company_ref ) result
where Calls > 18
CallerID refers to Caller table not Customer table
SELECT Y.Company_name, Count(I.Call_ref)
FROM Issue I
JOIN Customer C
ON I.Caller_id = C.Caller_id
JOIN Company Y
ON C.Company_ref = Y.Company_ref
Group by Company_name

How to join any number of tables in MySQL?

I am having a major problem joining 5 tables because each table only has 1 column in common with only 1 other table.
Here are my tables and columns in each table:
TABLE (COLUMNS)
person (person_id, first_name, last name)
building (building_id, building_name)
room (room_id, room_number, building_id, capacity)
meeting (meeting_id, room_id, meeting_start, meeting_end)
person_meeting (person_id, meeting_id)
OK, now here is what I am trying to do (pasted from a homework assignment):
Construct the SQL statement to find all the meetings that person_id #1 has to attend. Display the following columns:
Person’s first name
Person’s last name
Building name
Room number
Meeting start date and time
Meeting end date and time
Now I know how to join 2 tables but I have no idea how to pull info from 5 different tables like this.
I tried looking up how to do this and it just says to do a UNION command, and I am just learning and have yet to cover that.
As UNION is used to combine the result from multiple SELECT statements into a single result set, you don't need it for this scenario. You have to join all the tables one by one based on their Id.
SELECT P.First_Name, P.Last_Name, B.Building_name, R.Room_Number,
M.Meeting_Start, M.Meeting_End FROM Person P
JOIN Person_Meeting PM ON P.Person_Id = PM.Person_Id
JOIN Meeting M ON PM.Meeting_Id = M.Meeting_Id
JOIN Room R ON M.Room_Id = R.Room_Id
JOIN Building B ON R.Building_Id = B.Building_Id
WHERE P.Person_Id = 1

How to select all joined rows from 2 tables including null

I currently have 2 tables, vaccination and vaccination_type which can be joined using vaccine_id attribute.
vaccination is basically the joining many-to-many table between vaccination_type and patient. It has 3 attributes: vaccine_id, registration_no and date. Vaccination_type has vaccine_id, vaccine_name.
I want to join these 2 tables and get these entries:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A 1112 2015-01-15
This means that even the patient has not done hepatitis B vaccination, I still want the entry to be null. But I only want the one to specific registration_no, in this case 1111, in other words I also want the hepatitis A is written as null, as it is not done by 1111. So my expected result if I am dealing with 1111 will be:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A null null
I have tried using all of the join, but cannot find the one that works perfectly. Any suggestion will be appreciated, thanks!
If you want the information for one registration, then that should be your base table. Then you want to get all vaccination types for that registration. Finally join onto the vaccination table to get the date it was administered, or NULL if that person never got it:
SELECT
registrations.registration_no,
vaccination_type.vaccine_id,
vaccination_type.name,
vaccination.date
FROM
registrations
CROSS JOIN vaccination_type
LEFT JOIN vaccination ON registrations.registration_no = vaccination.registration_no AND vaccination_type.vaccine_id = vaccination.vaccine_id
WHERE
registrations.registration_no = 1111
Fiddle: http://sqlfiddle.com/#!2/e8b2d/5
Use LEFT JOIN it join both tables.
Try this:
SELECT VT.vaccine_id, VT.vaccine_name, V.registration_no
FROM Vaccination_type VT
LEFT JOIN vaccination V ON VT.vaccine_id = V.vaccine_id;
As I now understand the question, you need a listing of all vaccinations against which you will need to see all patients (or a particular patient) to see which vaccinations a patient has and has not taken. Yes, that is possible, but it requires all three tables. The Vaccination_Type table has the list of all vaccinations, the Patients table has the list of all patients. The Vaccinations table has the list of vaccinations that patients have taken.
The first step is to create a Cartesian product of all vaccinations with all patients. This will give you a list of all vaccinations and for each vaccination a list of all patients.
select vt.vaccine_id, p.registration_no
from Vaccination_Type vt
cross join Patients p;
Then left join with the existing cross table.
select vt.vaccine_id, p.registration_no, v.vaccination_date
from Vaccination_Type vt
cross join Patients p
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = p.registration_no;
This gives you all vaccinations with all patients. If the patient has received the vaccination, it will be followed by the date of that vaccination. If not, the date field will be NULL.
If this is something you need periodically, like for reporting, you can create a view of the above query and just select from the view.
If you are only interested in seeing this listing in regards to a particular patient, you can select from the view:
select vaccine_id, registration_no, vaccination_date
from All_Vaccinations
where registration_no = 1111;
However, if a particular patient's vaccination history is mostly what you want, you can obtain that with just two tables with a little better performance:
select vt.vaccine_id, v.vaccination_date
from Vaccination_Type vt
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = 1111;
Notice that registration_no is left out of the select list as we are no longer using the Patients table. We could have used the registration_no field from the Vaccinations table but then if the patient is missing a vaccination, not only the date field but also the registration_no field of the result set would contain NULL. There is no reason to expose a field that A) you already know the value of and B) will have the proper value in some rows and NULL in others. That could lead to some confusion. When you are looking at the data for more than one patient at a time, you must use the three-table join as the registration_no would be required to make sense of the output.

Marking Records as duplicates in mySQL

I am not a databases guy,but I have been given the "fun" job of cleaning up someone else's database. We have many duplicate record in our databases and some of customers are getting double or triple billed every month.
Given the following Database example
:
Table: Customers
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 No
5 Tree Top 2127657654 No
Is it possible to write a query that Identifies the all duplicate phone numbers (in this case records 1 and 4) and then marks and duplicate records yes by updating the DoNotBill column. But leaves the first record unmarked.
In this example case we would be left with:
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 Yes
5 Tree Top 2127657654 No
something like this?
UPDATE
customers cust,
(SELECT
c1.ID,
c1.name,
c1.phone,
c1.DoNotBill
FROM customers c
LEFT JOIN
(SELECT
cc.ID
FROM customers cc
) as c1 on c1.phone = c.phone
) dup
SET cust.DoNotBill = 'Yes' WHERE cust.id=dup.id ;
To begin with I assume that the DoNotBill column only has two possible values; yes and no. In that case it should be bool instead of varchar, meaning it would be either true or false.
Furthermore I don't get the meaning of the DoNotBill column. Why wouldn't you just use something like this?
select distinct phone from customers
SQL SELECT DISTINCT
That would give you the phone numbers without duplicates and without the need for an extra column.
This depends on ur data amount
You can do it in steps and make use some tools like excel...
This qrt
SELECT a.id,b.id,a.phone FROM clients a , clients b WHERE
A.phone =b.phone
And a.id!=b.id
The result is all duplicated records.
Add
Group by a.phone
And u will get 1 record for each 2 duplicates.
if you like the records and they are whT u need. ChNge select to select a.id and
Use this qry as subqry to an update sql statement
UPDATE clients SET billing='no' WHERE id IN ( sql goes here)
UPDATE customers c SET c.DoNotBill="Yes";
UPDATE customers c
JOIN (
SELECT MIN( ID ) ID, Phone
FROM customers
GROUP BY Phone
) u ON c.ID = u.ID AND c.Phone = u.Phone
SET c.DoNotBill="No";
That way not only duplicates are eliminated, but all multiple entries are dealt with.

3 Table Join with SUM and GROUP BY not working

I have three tables that I'm working with.
AccountingLine - Holds the generic account details
Budget - Holds the budget data for each AccountingLine (Many rows per AccountingLine)
Actual - Holds the actual cost data for each AccountingLine (Many rows per AccountingLine)
I'm trying to get the results in a single query which will return ALL ROWS from the AccountingLine table, and SUM the Amounts for each AccountingLine from the Budget and Actuals table.
Using the SQL below, the SUM isn't working for the Budget or Actual data. If I remove one of the joins and one of the SUM functions then it calculates correctly for the single joined table. Very strange... anyone run across this with multiple SUM functions on three or more tables in MySQL?
SELECT A.*, SUM(B.`amount`) AS BudgetAmount, SUM(ACT.`amount`) as ActualAmount
FROM accounting_line A
LEFT JOIN budget B ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN actual ACT ON ACT.accounting_line_id = A.accounting_line_id
GROUP BY A.`accounting_line_id`
By issuing the statement above, I'd expect to see the accounting_line fields, the SUM of the Budget amounts for each accounting_line and the SUM of the Actual amounts for each accounting_line.
I've searched all over and can't find an instance of multiple SUM functions. Thanks so much for any advice.
Josh
Table Data is below:
Table: AccountingLine
act_line_id department
----------------------------------
1 Sales
2 HumanResources
Table: Budget
budget_id actg_line_id amount
----------------------------------------------
1 1 3500.00
2 2 5000.00
3 2 15000.00
Table: Actual
actual_id actg_line_id amount
----------------------------------------------
1 1 1000.00
2 2 500.00
3 2 9000.00
A join repeats each matching row in the other table. So if you have 3 rows in three tables and join them together, you end up with 9 rows. If you sum, each sum from the second and third table is 3x too high.
One solution is to sum in a subquery, so that the join only finds one row:
SELECT A.*
, B.SumAmount as BudgetAmount
, ACT.SumAmount as ActualAmount
FROM accounting_line A
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from budget
group by
accounting_line_id
) as B
ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from actual
group by
accounting_line_id
) as ACT
ON ACT.accounting_line_id = A.accounting_line_id
try this modified one, calculate it's totals on a subquery
SELECT a.*, b.totalBudget, c.totalActual
FROM AccountingLine a LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalBudget
FROM Budget
GROUP BY actg_line_id
) b on a.act_line_id = b.actg_line_id
LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalActual
FROM Actual
GROUP BY actg_line_id
) c on a.act_line_id = c.actg_line_id
SQLFiddle Demo
Try this
Select A.* ,SUM(B.Amount) As BudgetAmount,SUM(Act.Amount) As ActualAmount
from AccountingLine A
INNER JOIN Budget B
ON B.budget_id = A.actg_line_id
INNER JOIN Actual Act
ON Act.actual_id = A.accounting_line_id
Grounp By A.accounting_line_id