How can I get join to work with conditions - sql

I have a table TimeIntervals with a relationship to Breaks with in turn has a relationship to DeletedBreaks.
What i'm trying to do is to receive al rows (Breaks) that has timeIntervals id and no deleted break for a given date.
That is if a break has no deleted breaks, no row in DeletedBreaks table for a break id Or if there is a row with that breaks id but not the given date, than that break should be returned.
Following is not working but you might understand what i'm trying to do:
SELECT B.*
FROM Breaks B
JOIN TimeIntervals T
ON B.TimeIntervalId = T.Id
JOIN DeletedBreaks DB
ON (
(
DB.BreakId = B.Id
AND DB.DeletedDate <> '2014-10-13'
)
OR DB.BreakId IS NULL
)
AND (T.Id = 2)

Use a LEFT JOIN to your DeletedBreaks table instead of an inner join since you don't want to drop Break records just becasue the DeletedBreaks ID is null.
To test for NULL DeletedBreaks or DeletedBreaks for a particular day, do so in the WHERE clause:
SELECT B.*
FROM Breaks B
JOIN TimeIntervals T
ON B.TimeIntervalId = T.Id
LEFT JOIN DeletedBreaks DB ON
DB.BreakId = B.Id
WHERE
(DB.DeletedDate <> '2014-10-13'
OR DB.BreakId IS NULL)
AND T.Id = 2

Related

SQL: How to remove duplicate rows created by CASE WHEN statement

I have two tables: (A) customers of the gym and (B) customers of the restaurant. I want to create an indicator in table (A) to indicate the customers who have been to both the gym and the restaurant on the same day. In accomplishing this, I used the following SQL script, but it created duplicate rows:
SELECT *,
CASE WHEN a.GymDate = b.RestaurantDate THEN 'Meal + Gym on the same day'
ELSE 'Gym Only' END AS 'Meal+Gym'
FROM Table_A a
LEFT JOIN Table_B b
ON a.customerid = b.customerid;
May I know how to keep only Table_A, but with the addition of the 'Meal+Gym' Indicator? Thanks!
A case expression does not generate rows, it is your join that is generating the duplicate rows. You could add the date predicate to the join condition, and merely check for the existence of a record, e.g.
SELECT *,
CASE WHEN b.customerid IS NOT NULL THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS [Meal+Gym]
FROM Table_A a
LEFT JOIN Table_B b
ON a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate;
If table_B is not unique per customer/Date then you may need to do something like this to prevent duplicates:
SELECT *,
CASE WHEN r.RestaurantVisit IS NOT NULL THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS [Meal+Gym]
FROM Table_A a
OUTER APPLY
( SELECT TOP 1 1
FROM Table_B b
WHERE a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate
) AS r (RestaurantVisit);
N.B. While using single quotes works for column aliases, it is not a good habit at all, because it makes your column aliases indistinguishable from string literals other than from context. Even if this is clear to you, it probably isn't to other people, and since there's about a 10:1 ratio of reading:writing code, writing code that is easy to read is important. As such I've used square brackets for your column name instead
I would start with a table of customers, so you get an indicator for customers who have been to neither the gym nor a restaurant.
Then:
select c.*,
(case when exists (select 1
from table_a a join
table_b b
on a.customerid = b.customerid and
a.GymDate = b.RestaurantDate
where a.customerid = c.customerid
)
then 1 else 0
end) as same_day_gym_restaurant_flag
from customers c;
You can use CASE WHEN EXISTS instead of the LEFT JOIN:
SELECT *,
CASE WHEN EXISTS (
SELECT 1 FROM Table_B b
WHERE a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate)
THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS 'Meal+Gym'
FROM Table_A a
This assumes that you don't need any data from Table_B in the results.

Duplicating latest record in next months until there's a change

I'm trying to query ids and their type every month but I only have two tables of which one contains all ids and the other one contains type changes transactions. When I joined them, it gave me null values for months with no change.
SELECT a.month_date, a.id, b.type
FROM id_table a
LEFT JOIN type_changes_table b
ON a.id = b.id AND a.month_date = b.month_date
Is there a way using Presto SQL/amazon athena to duplicate the latest value to the next months until there is no change (ex. expected column in the picture below).
One method uses last_value(ignore nulls):
SELECT i.month_date, i.id, c.type,
LAST_VALUE(c.type IGNORE NULLS) OVER (PARTITION BY i.id ORDER BY i.month_date) as imputed_type
FROM id_table i LEFT JOIN
type_changes_table c
ON i.id = c.id AND i.month_date = c.month_date;

SQL Table Joining

I'm joining these three tables, but the same information gets displayed 3 times ... Any idea how to have only the unique rows to be displayed, as determined by unique shipment id's?
SELECT S.SHIPMENT_ID, S.CREATION_DATE, S.BUSINESS_ID, B.BUS_ID, S.SHIPMENT_STATUS, S.BUSINESS_NAME, S.SHIPMENT_MODES, S.CUSTOMER_NAME
FROM "SHIPMENT" S
INNER JOIN "BUSINESS" B ON S.BUSINESS_ID=B.BUS_ID
INNER JOIN "SHIPMENT_GROUP" SG ON S.SHIPMENT_ID=SG.SHIPMENT_ID
INNER JOIN "DATA_GROUP" DG ON DG.ID=SG.GROUP_ID
try select distinct
SELECT DISTINCT column1, column2, ...
FROM table_name;
w3schools
You are selecting rows from the first table only, so this suggests that you are using the joins for filtering.
If so, you can rewrite this with exists, which will avoid duplicates if there are multiple matches. Starting from your existing query, the logic would be:
select s.*
from shipment s
where
exists (
select 1
from business b
where b.bus_id = s.business_id
) and exists (
select 1
from shipment_group sg
inner join data_group dg on dg.id = sg.group_id
where sg.shipment_id = s.shipment_id
)

SQL joining tables (a,b) - but only rows from a

I want to join two tables (sales_flat_order & sales_flat_order_address). I want to use the entity_id from one table to match with the parent_id in the other table. Now there is one row for every entity_id but there are two rows for every parent_id. But i just want to have one row after the join.
I am using this Statement but it still returns two rows for every entity_id after the join.
select o.customer_email, a.prefix
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
Does anybody have an idea?
select o.customer_email,
a.prefix
from sales_flat_order o
left JOIN (Select distinct a.prefix, a.parentid
from sales_flat_order_address) a
on o.entity_id = a.parent_id
Just add a distinct?
select DISTINCT o.customer_email, a.prefix
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
Obviously this will only remove multiple rows where o.customer_email, a.prefix are in fact the same, otherwise you would need to find some way to decide which record you actually wanted... eg:
select o.customer_email, MIN(a.prefix)
from sales_flat_order o
left JOIN sales_flat_order_address a on o.entity_id = a.parent_id
group by o.customer_email
SELECT o.customer_email, oa.prefix
FROM sales_flat_order o
LEFT JOIN
sales_flat_order_address oa
ON oa.entity_id =
(
SELECT entity_id
FROM sales_flat_order_address oai
WHERE oai.parent_id = o.entity_id
ORDER BY
FIND_IN_SET(address_type, 'billing,shipping')
LIMIT 1
)
This way you will be getting the prefix from billing address (if it's set), falling back to shipping address if the billing address is not set.

Help converting subquery to query with joins

I'm stuck on a query with a join. The client's site is running mysql4, so a subquery isn't an option. My attempts to rewrite using a join aren't going too well.
I need to select all of the contractors listed in the contractors table who are not in the contractors2label table with a given label ID & county ID. Yet, they might be listed in
contractors2label with other label and county IDs.
Table: contractors
cID (primary, autonumber)
company (varchar)
...etc...
Table: contractors2label
cID
labelID
countyID
psID
This query with a subquery works:
SELECT company, contractors.cID
FROM contractors
WHERE contractors.complete = 1
AND contractors.archived = 0
AND contractors.cID NOT IN (
SELECT contractors2label.cID FROM contractors2label
WHERE labelID <> 1 AND countyID <> 1
)
I thought this query with a join would be the equivalent, but it returns no results. A manual scan of the data shows I should get 34 rows, which is what the subquery above returns.
SELECT company, contractors.cID
FROM contractors
LEFT OUTER JOIN contractors2label ON contractors.cID = contractors2label.cID
WHERE contractors.complete = 1
AND contractors.archived = 0
AND contractors2label.labelID <> 1
AND contractors2label.countyID <> 1
AND contractors2label.cID IS NULL
When doing a LEFT JOIN, you need to put all conditions of the JOIN into the ON clause.
In your example you get NULL for left joined columns that do not exist, but you then compare them to values again (<> 1) which does not work.
SELECT c.company, c.cID
FROM contractors c
LEFT JOIN contractors2label c2
ON ( c2.cID = c.cID AND c2.labelID <> 1 AND c2.countyID <> 1 )
WHERE c.complete = 1
AND c.archived = 0
AND c2.cID IS NULL
BTW: Using aliases (like c in my example) makes reading and writing your queries easier.
When you restrict on a where clause using the columns in a table that's LEFT joined, you are effectively removing the LEFT OUTER part of the join, because you're filtering on columns that have to be there. Try this instead:
SELECT company, contractors.cID
FROM contractors
LEFT OUTER JOIN contractors2label
ON (contractors.cID = contractors2label.cID
AND contractors2label.labelID <> 1
AND contractors2label.countyID <> 1)
WHERE contractors.complete = 1
AND contractors.archived = 0
AND contractors2label.cID IS NULL
This does the restriction as part of the join, so nulls can still be used in the larger query.