Do I use inner join or left join? - sql

I have two separate tables I'm pulling data from with the associate_id being the primary key. I'm trying to find all sales(sales_charge found in sales.dim) made by Associate_ID over several transactions within the last 4 months and the last year. I'm having a hard time with the time stamp and the joins.
Here's what I have so far:
SELECT associate_id
, sales.dim.sales_charge
FROM dbo.associate
LEFT JOIN dbo.sales ahd
ON associate_id = ahd.associate_id
AND ahd.end_dt > GETDATE()
I'm new to SQL and coding in general, please let me know what I'm missing.
Thanks

If you want to include all associates, even those with no sales, then use left join:
SELECT a.associate_id, ahd.dim.sales_charge
FROM dbo.associate a LEFT JOIN
dbo.sales ahd
ON a.associate_id = ahd.associate_id AND
ahd.end_dt > DATEADD(month, -4, GETDATE());

Answer to the question "Do I use inner join or left join?" is that you use inner join when you want to include only matching records from both tables, whereas left (outer) join will include all the records from left side table.
In the query you are attempting if you want to have all associates included in the result set even if they do not have any sales in last 4 months, use LEFT JOIN. If you want to have only those associates who have one or more sales then use INNER JOIN.
Another problem is with the condition "ahd.end_dt > GETDATE()". This means all end date after current time. Change it to "ahd.end_dt > DATEADD(month, -4, GETDATE())"

Related

SQL: Left Join with three tables

I have three tables
Products (idProduct, name)
Invoices(typeinvoice, numberinvoice, date)
Item-invoices(typeinvoice, numberinvoice, idProduct)
My query has to select all the products not selled in the year 2019. I can use a function to obtain the year from the date, for example year(i.date). I know that the products that don't appear in the Item-invoice table are the not selled products. So I have tried with this two codes and obtain a good output.
SELECT p.name
FROM Products p
EXECPT
SELECT ii.idProduct
FROM Item-invoices ii, Invoices i
WHERE ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND year(i.date)=2019
And the other code use a sub-query:
SELECT p.name
FROM Products p
WHERE p.idProduct NOT IN
(SELECT ii.idProduct
FROM Item-invoices ii, Invoices i
WHERE ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND year(i.date)=2019)
The answer is how can i use the left join command to have the same output. I've tried with
SELECT p.name
FROM Products p
LEFT JOIN Item-invoices ii ON
p.IdProduct=ii.idProduct
LEFT JOIN Invoices i ON
ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
WHERE year(i.date)=2019
AND ii.idProduct IS NULL
I know this is wrong but can't find the solution
Any help?
You are almost there. You just need to move the condition on the invoice date to from the from clause to the on clause of the join.
Conditions in the WHERE clause are mandatory, so what you did actually turned the LEFT JOI to an INNER JOIN, which can never be fulfilled (since both conditions in the WHERE clause cannot be true at the same time).
SELECT p.name
FROM Products p
LEFT JOIN Item-invoices ii ON
p.IdProduct=ii.idProduct
LEFT JOIN Invoices i ON
ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND i.date >= '2019-01-01'
AND i.date < '2020-01-01'
WHERE ii.idProduct IS NULL
Note that I changed your date filter to a half-open filter that operates directly on the stored date, without using date functions; this is a more efficient way to proceed (since it allows the database to use an existing index).

Full Outer Join Not including all records from both sides

I have a calendar table with dates from 2000 through 2029. I have done a full outer join with my data table on date=tdate. My goal is to count trips occurring by date and to show a zero if there are no trips. However, all dates are not showing up. If there is no trip date in the data table, the date from the Calendar table does not show up at all. I've never seen this happen before. I've tried changing it to a left outer join which didn't work either. Actual query below.
SELECT DISTINCT c.DATE,
COALESCE(COUNT(t.TripID), 0) AS tripCount
FROM dbo.Calendar c
FULL OUTER JOIN dbo.TripsMade t ON c.DATE = CONVERT(DATE, t.tripdate, 126)
WHERE (c.DATE BETWEEN '2019-09-01' AND '2019-09-30' )
AND (t.compid = 270 OR t.compid IS NULL)
GROUP BY c.DATE
ORDER BY c.DATE
If I select all dates from the Calendar Table, they are all there. There are dates missing from the Data Table, and always will have some missing. I need all dates to show up on this report and show a zero if there are no associated records on the Data Table side.
What am I missing here?
Goal
DATE TOTAL_TRIPS
2019-09-01 3
2019-09-02 5
2019-09-03 0 <== This row is currently missing completely
2019-09-04 4
2019-09-05 0 <== This row is currently missing completely
2019-09-06 9
Your WHERE clause ends up filtering non-matches out from both tables, turning them almost into INNER JOINs (it is a little complicated with the condition on t.
I don't think you need a FULL JOIN. I suspect you want a LEFT JOIN with filtering (for the second table) in the ON clause:
SELECT c.Date, COUNT(t.TripID) AS tripCount
FROM dbo.Calendar c LEFT OUTER JOIN
dbo.TripsMade t
ON c.date = CONVERT(DATE, t.tripdate, 126) AND
t.compid = 270
WHERE c.Date BETWEEN '2019-09-01' AND '2019-09-30'
GROUP BY c.Date
ORDER BY c.Date;
If compid can actually take on NULL values and you want them, then use (t.compid = 270 OR t.compid IS NULL). I suspect, though, that is not what you want. The NULL just represents a non-match on the JOIN.
Notes:
SELECT DISTINCT is almost never used with GROUP BY, and it is not needed in this case.
COUNT() does not return NULL, so there is no need for COALESCE().
Filtering on the first table does belong in the WHERE clause.

SQL Query to count the records

I am making up a SQL query which will get all the transaction types from one table, and from the other table it will count the frequency of that transaction type.
My query is this:
with CTE as
(
select a.trxType,a.created,b.transaction_key,b.description,a.mode
FROM transaction_data AS a with (nolock)
RIGHT JOIN transaction_types b with (nolock) ON b.transaction_key = a.trxType
)
SELECT COUNT (trxType) AS Frequency, description as trxType,mode
from CTE where created >='2017-04-11' and created <= '2018-04-13'
group by trxType ,description,mode
The transaction_types table contains all the types of transactions only and transaction_data contains the transactions which have occurred.
The problem I am facing is that even though it's the RIGHT join, it does not select all the records from the transaction_types table.
I need to select all the transactions from the transaction_types table and show the number of counts for each transaction, even if it's 0.
Please help.
LEFT JOIN is so much easier to follow.
I think you want:
select tt.transaction_key, tt.description, t.mode, count(t.trxType)
from transaction_types tt left join
transaction_data t
on tt.transaction_key = t.trxType and
t.created >= '2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
Notes:
Use reasonable table aliases! a and b mean nothing. t and tt are abbreviations of the table name, so they are easier to follow.
t.mode will be NULL for non-matching rows.
The condition on dates needs to be in the ON clause. Otherwise, the outer join is turned into an inner join.
LEFT JOIN is easier to follow (at least for people whose native language reads left-to-right) because it means "keep all the rows in the table you have already read".

T-SQL programming

I need to write a query to pull out the records as below.
membership_id,
person_id,
first_name
last_name
who joined or called yesterday and the members who didn't join will have only person_id
But the below query is pulling out all the records from the table.
SELECT
dbo.pn_membership.membership_id,
dbo.pn.person_id,
dbo.pn.first_name,
dbo.pn.surname,
dbo.pn.create_datetime
FROM
dbo.pn
LEFT OUTER JOIN
dbo.pn_membership
ON dbo.pn.person_id=dbo.pn_membership.person_id AND
dbo.pn.create_datetime >=getdate()-1
I need the records only for the day before the run date.
Try this version.
Your write in your question "who joined or called yesterday"
but your query does "who joined or called in the last 24 hours"
which is kind of different. Also, as Sparky noted you had this lack
of WHERE clause problem. My version does "who joined or called yesterday".
SELECT
dbo.pn_membership.membership_id,
dbo.pn.person_id,
dbo.pn.first_name,
dbo.pn.surname,
dbo.pn.create_datetime
FROM
dbo.pn
LEFT OUTER JOIN
dbo.pn_membership
ON dbo.pn.person_id=dbo.pn_membership.person_id
WHERE
dbo.pn.create_datetime >= DATEADD(Day, DATEDIFF(Day, 0, getdate()), -1)
AND
dbo.pn.create_datetime < DATEADD(Day, DATEDIFF(Day, 0, getdate()), 0)
are you saying that it pulls records with a date < getdate()-1 or that it pulls records where
person_id is Null ?
if the latter try this
SELECT dbo.pn_membership.membership_id, dbo.pn.person_id, dbo.pn.first_name,
dbo.pn.surname, dbo.pn.create_datetime FROM dbo.pn LEFT OUTER JOIN dbo.pn_membership ON
dbo.pn.person_id=dbo.pn_membership.person_id AND dbo.pn.create_datetime >=getdate()-1
and dbo.pn.person_id is not NULL
Try this:
SELECT
dbo.pn_membership.membership_id,
dbo.pn.person_id,
dbo.pn.first_name,
dbo.pn.surname,
dbo.pn.create_datetime
FROM
dbo.pn
LEFT OUTER JOIN
dbo.pn_membership
ON dbo.pn.person_id=dbo.pn_membership.person_id
WHERE dbo.pn.create_datetime >=getdate()-1
Your query says...
Give me some fields from the pn table.
Also, if the person has matching membership record, give me that
information
if they don't give me the fields from the membership table with NULL
values
By moving the date test condition to the WHERE clause, you are reducing the rows from the pn table. By applying date as part of the JOIN, you are only increasing the likelihood of getting more NULL value columns from the membership table..
It looks like the problem is at the very end of your query. Instead of AND dbo.pn.create_datetime >=getdate()-1, try WHERE dbo.pn.create_datetime >=getdate()-1. Including your filter criteria as part of the OUTER JOIN statement isn't the same thing as using a WHERE clause. See SQL Standard Regarding Left Outer Join and Where Conditions also.

Left and right joining in a query

A friend asked me for help on building a query that would show how many pieces of each model were sold on each day of the month, showing zeros when no pieces were sold for a particular model on a particular day, even if no items of any model are sold on that day. I came up with the query below, but it isn't working as expected. I'm only getting records for the models that have been sold, and I don't know why.
select days_of_months.`Date`,
m.NAME as "Model",
count(t.ID) as "Count"
from MODEL m
left join APPLIANCE_UNIT a on (m.ID = a.MODEL_FK and a.NUMBER_OF_UNITS > 0)
left join NEW_TICKET t on (a.NEW_TICKET_FK = t.ID and t.TYPE = 'SALES'
and t.SALES_ORDER_FK is not null)
right join (select date(concat(2009,'-',temp_months.id,'-',temp_days.id)) as "Date"
from temp_months
inner join temp_days on temp_days.id <= temp_months.last_day
where temp_months.id = 3 -- March
) days_of_months on date(t.CREATION_DATE_TIME) =
date(days_of_months.`Date`)
group by days_of_months.`Date`,
m.ID, m.NAME
I had created the temporary tables temp_months and temp_days in order to get all the days for any month. I am using MySQL 5.1, but I am trying to make the query ANSI-compliant.
You should CROSS JOIN your dates and models so that you have exactly one record for each day-model pair no matter what, and then LEFT JOIN other tables:
SELECT date, name, COUNT(t.id)
FROM (
SELECT ...
) AS days_of_months
CROSS JOIN
model m
LEFT JOIN
APPLIANCE_UNIT a
ON a.MODEL_FK = m.id
AND a.NUMBER_OF_UNITS > 0
LEFT JOIN
NEW_TICKET t
ON t.id = a.NEW_TICKET_FK
AND t.TYPE = 'SALES'
AND t.SALES_ORDER_FK IS NOT NULL
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
GROUP BY
date, name
The way you do it now you get NULL's in model_id for the days you have no sales, and they are grouped together.
Note the JOIN condition:
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
instead of
DATE(t.CREATION_DATE_TIME) = DATE(days_of_months.`Date`)
This will help make your query sargable (optimized by indexes)
You need to use outer joins, as they do not require each record in the two joined tables to have a matching record.
http://dev.mysql.com/doc/refman/5.1/en/join.html
You're looking for an OUTER join. A left outer join creates a result set with a record from the left side of the join even if the right side does not have a record to be joined with. A right outer join does the same on the opposite direction, creates a record for the right side table even if the left side does not have a corresponding record. Any column projected from the table that does not have a record will have a NULL value in the join result.