LEFT JOIN include data - sql

I have an application which handles school vacation. Unfortunately there are three kinds of different school vacations: Country wide, Federal State wide and City wide vacations. I store all the information in a table days, a table vacation_periods and a connection table slots:
days {
id:integer
date_value:date
}
slots {
id:integer
day_id:integer
vacation_period_id:integer
}
vacation_periods {
id:integer
starts_on:date
ends_on:date
name:string
country_id:integer
federal_state_id:integer
city_id:integer
}
I want to select all days within a specific time frame. Let's say Jan 1st of 2017 to Jan 31st of 2017. I can get those days with:
SELECT * FROM days WHERE date_value >= '2017-01-01' AND
date_value <= '2017-01-31';
But for my vacation calendar I don't just need the days but also the information which vacation_periods are within. Assuming I search for all vacation_periods which are in that time frame and which have
country_id == 1 or federal_state_id == 5 or city_id == 30
I've read about JOINS and LEFT JOINS which seem to be the solution to the problem. But I can't get everything together.
Is it possible to send one SQL request which returns all days within the requested time frame and the additional information if a vacation_period that fits the country_id == 1 or federal_state_id == 5 or city_id == 30 rule is connected via slots to each day. Including the name of that vacation_period?
If one request is not possible: Which is the quickest way to solve this within the database? How many requests? What kind of requests?
If possible I'd like to get a result in some kind of this form:
- date_value: "2017-01-01"
- date_value: "2017-01-02"
- date_value: "2017-01-03"
* vacation_period.id: 15
* vacation_period.name: "foobar"
- date_value: "2017-01-04"
* vacation_period.id: 15
* vacation_period.name: "foobar"
- date_value: "2017-01-05"
* vacation_period.id: 15
* vacation_period.name: "foobar"
- date_value: "2017-01-06"
- date_value: "2017-01-07"
...

The following query might give you the answer you are looking for:
SELECT * FROM days WHERE date_value >= '2017-01-01' AND date_value <='2017-01-31'
INNER JOIN slots ON days.id = slots.day_id
INNER JOIN vacation_periods ON vacation_periods.id = slots.vacation_period_id

I think you can get an unformatted version of what you want (that could be processed into a hierarchical output) with
CREATE TYPE vacation_authority AS ENUM
('COUNTY', 'FED-STATE', 'CITY');
/* not necessary, but cleans up the vacation_period table */
change to let vacation_period have only one id, and a new field authority of type vacation_authority. You can now make a primary key out of either the id field or (id, authority), depending on how the vacation data comes into the system.
SELECT date_value, vp.name, vp.id /* is the ID meaningful or arbitrary? */
FROM dates LEFT JOIN vacation_periods vp
WHERE date_value BETWEEN vp.starts_on AND vp.ends_on; -- inclusive range
Now if there are multiple holidays spanning a given date, this will be multiple records in the output. It's not clear what you want in this case.

None of the other answers was able to solve my problem but they let me to the solution so I'm grateful for them. Here's the solution:
SELECT days.date_value, slots.period_id, vacation_periods.name FROM days
LEFT OUTER JOIN slots ON (days.id = slots.day_id)
LEFT OUTER JOIN vacation_periods ON (slots.period_id = vacation_periods.id)
WHERE days.date_value >= '2017-01-05'
AND days.date_value <='2017-01-15'
AND (vacation_periods.id IS NULL
OR vacation_periods.country_id = 1
OR vacation_periods.federal_state_id = 5)
ORDER BY days.date_value;

Related

Transforming SQL many to many JOIN with a constraint into DAX/PowerBI

I'm trying recreate a join between 2 tables, timeregistration and schedule, into one table which I then filter based the difference between the scheduled start time and the actual start time. Basically; I'm trying to determine whether a employee is late or early.
My query in SQL looks like this and works as desired, I just can't seem to figure out how to make this join work in DAX for Power BI, let alone the time constraint.
SELECT
tr.employee_employeeID AS EmployeeID,
tr.rawstarttime AS Actual_Start,
s.StartTime_Schedule AS Schedule_Start
FROM
timeregistration tr,
schedule s
WHERE
tr.Employee_EmployeeID = s.Employee_EmployeeID
AND
DATEDIFF(MINUTE, tr.RawStartTime, s.StartDateTime) < 60
AND
DATEDIFF(MINUTE, tr.RawEndTime, s.StartDateTime) > -60
And the tables and relation look something like this:
Table structure
I have tried the following so far:
GENERATE(timeregistration; schedule)
- which returned
The Column with the name of 'EmployeeID' already exists in the 'date_diff' Table.
NATURALINNERJOIN(timeregistration; schedule)
- which returned
No common join columns detected. The join function 'NATURALINNERJOIN' requires at-least one common join column.
CROSSJOIN(timeregistration; schedule)
- which returned
The Column with the name of 'EmployeeID' already exists in the 'date_diff' Table.
As of right now I wouldn't really know what to do with the JOIN, so any help would be appreciated.
With kind regards,
Martien
(edit: Fixed formatting mistakes)
Solved it with this DAX query
date_diff =
FILTER(
ADDCOLUMNS(
GENERATEALL(
schedule;
VAR schedule_id = schedule[EmployeeID]
RETURN
SELECTCOLUMNS(
CALCULATETABLE(
timeregistration;
timeregistration[EmployeeID] = schedule_id
);
"StartTime_timeregistration"; timeregistration[StartTime_actual]
)
);
"diff"; DATEDIFF([StartTime_schedule];[StartTime_timeregistration];SECOND)
);
3600 >= [diff] &&
-3600 <= [diff] &&
NOT(ISBLANK([StartTime_timeregistration]))
)

SQL | Match Shop cash with Bank Cash

! - I'm not looking for paid software which will do this job (as too expensive)
We have an issue with cash management to match the values.
I have two SQL Tables, let's call it SHOP_CASH and BANK_CASH
1) The matching should be happens based on ShopName-CashAmount-Date.
2) Here I faced two issues
The cash should be round up to nearest £50, ideally, 12 400 and 12 499 should round up to 12 450, OR this just IDEAL is a match based on cash difference which less than 50, dry to match different value if the difference is less than 50, match them, but here is the question how to match the value up.. this is just the stupid ideas))??? Hmmm...stuck.
Dates, the shop can cash up a few days later, so need to join based on cash-up date (for example 2018-10-26) with bank date RANGE 2018-10-26 to (+7 days) 2018-11-02
Currently, I do not understand the possible way (logical) of matching in this circumstance. Any logical path of calculation/joining will be extremely appreciated
TRY:
Let's say I can join two tables by SHOPNAME - Cool
Then I will try to join by date, which potentially will be:
SELECT * FROM SHOP_CASH AS SC
LEFT JOIN BANK_CASH AS BC
ON SC.SHOP_NAME_SC = BC.SHOP_NAME_BC
AND SC.DATE_SC = (ANY DATE FROM SC.DATE_SC TO SC.DATE_SC (+7 DAYS) = TO DATE_BC - not sure how)
AND FLOOR(SC.CASH_SC / 50) * 50 = FLOOR(BC_CASH_BC / 50) * 50
P.S. For this project will be using the Google Big Query.
This is my (temporary solution)
WITH MAIN AS(SELECT
CMS.Store_name AS STORE_NAME,
CMS.Date AS SHOP_DATE,
CMB.ENTRY_DATE AS BANK_DATE,
SUM(CMS.Cash) AS STORE_CASH,
SUM(CMB.AMOUNT) AS BANK_CASH
FROM `store_data` CMS
LEFT JOIN `bank_data` AS CMB
ON CMS.store_name = CMB.STRAIGHT_LOOKUP
AND FLOOR(CMS.Cash / 50) * 50 = FLOOR(CMB.AMOUNT / 50) * 50
AND CAST(FORMAT_DATE("%F",CMB.ENTRY_DATE) AS STRING) > CAST(FORMAT_DATE("%F",CMS.Date) AS STRING)
AND CAST(FORMAT_DATE("%F",CMB.ENTRY_DATE) AS STRING) <= CAST(FORMAT_DATE("%F",DATE_ADD(CMS.Date, INTERVAL 4 day)) AS STRING)
GROUP BY STORE_NAME,SHOP_DATE,BANK_DATE)
SELECT
MAIN2.*
FROM (
SELECT
ARRAY_AGG(MAIN ORDER BY MAIN.SHOP_DATE ASC LIMIT 1)[OFFSET(0)] AS MAIN2
FROM
MAIN AS MAIN
GROUP BY MAIN.SHOP_DATE, MAIN.STORE_CASH)
this is quite interesting case.
You haven't provided any sample data so I'm not able to test it, but this may work. Some modification may be required since not sure about date format. Let me know if there is an issue.
SELECT * FROM SHOP_CASH AS SC
LEFT JOIN BANK_CASH AS BC
ON SC.SHOP_NAME_SC = BC.SHOP_NAME_BC
AND SC.DATE_SC BETWEEN BC.DATE_BC AND DATE_ADD(BC.DATE_BC, DAY 7)
AND trunc(SC.CASH_SC, -2) + 50 = trunc(BC.CASH_BC,2) + 50

Refer to another table and return data adjacent to Max() result

I have the following two tables:
Using SQL Server 2012, I want to know the INTERVAL from the Hourly table where the MaxWaitTime and Split match what comes from the Daily table for each day. I am assuming I need to use a window function here, but I can't figure out the right answer.
There may be times where MaxWaitTime is 0 for an entire day, and thus all rows from the hourly table match. In this scenario, I would prefer a Null answer, but the earliest INTERVAL for that day would be fine.
There will also be times where multiple INTERVALs have the same wait time. In this scenario the first INTERVAL where the MaxWaitTime is present that day should be returned.
You can use outer apply if you want at most one match:
Looks like a simple left join should work between the tables. I'm simply going by the data shown above...
The query should look something like this. If the join fails, then a NULL will be returned. Give it a go..
select d.*, h.interval as maxinterval
from daily d outer apply
(select top 1 h.*
from hourly h
where convert(date, h.interval) = d.row_date and
h.split = d.split and
h.maxwaittime = d.maxwaittime
order by h.interval asc
) h;
If you want NULL for multiple matches, you can do something similar:
select d.*, h.interval as maxinterval
from daily d outer apply
(select top 1 h.callsoffered, h.split, max(h.interval) as maxinterval
from hourly h
where convert(date, h.interval) = d.row_date and
h.split = d.split and
h.maxwaittime = d.maxwaittime
group by h.maxwaittime, h.split
having count(*) = 1
) h;
Looks like a simple left join should work between the tables. I'm simply going by the data shown above...
The query should look something like this. If the join fails, then a NULL will be returned. Give it a go..
select daily.* ,hourly.callsoffered, hourly.interval as maxinterval
from daily
left join hourly
on convert(date,hourly.interval) = daily.row_date
and hourly.split = daily.split
and hourly.maxwaittime = daily.maxwaittime

MS Access SQL Date Range Query

I am working on a classroom reservation tool. A core component is the ability to compare the requested date range to the existing reservations, to ensure that there is no overlap. I've read through several date range related questions here, and studied Salman's explanation and implementation of Allen's interval algebra ( SQL Query to Find Overlapping (Conflicting) Date Ranges ) until I understood it. Here's a stripped-down version of what I came up with.
tblRooms
roomID room
5 110
30 178
tblReservations
reservedID fkRoom dateIn dateOut
1 5 3/10/2017 3/15/2017
2 5 3/1/2017 3/3/2017
4 5 4/1/2017 4/30/2017
SELECT DISTINCTROW tblRooms.roomID, tblRooms.room
FROM tblRooms LEFT JOIN tblReservations
ON tblRooms.roomID = tblReservations.fkRoom
WHERE NOT Exists (
SELECT DISTINCT tblRooms.roomID
FROM tblRooms
WHERE ((tblReservations.[dateOut] >= #3/3/2017#)
AND (#3/9/2017# >= tblReservations.[dateIn])));
I'm getting inconsistent returns. These dates will exclude room 110, as they should. Other test input (#3/4/2017# and #3/10/2017#, #4/1/2017# and #4/14/2017#) won't. I've tried combinations of "WHERE NOT (...", "WHERE Exists () = False", etc.
I work on a highly restrictive network, where I can't pull in templates at will - my only options when I create a database are "Blank" and "Web", so I've got to roll my own on this. I appreciate any assistance.
Can you try the following:
SELECT DISTINCTROW tblRooms.roomID, tblRooms.room
FROM tblRooms
WHERE NOT Exists (
SELECT 1
FROM tblReservations
WHERE
tblReservations.fkRoom = tblRooms.roomID
AND ((tblReservations.[dateOut] >= #3/3/2017#)
AND (#3/9/2017# >= tblReservations.[dateIn])));
For a reservation check query you would do this:
select ...
from tblRooms room
where not exists
( select *
from tblReservations r
where r.fkRoom = room.roomId and
end > r.[datein] and start < r.[dateout] );
BUT the important part is, pass those end and start as parameters instead of hardcoded values like you did. With hardcoded values you are always open to get wrong results or error. For example what is:
#3/9/2017# really? Its interpretation would depend on regional settings (I am not an access programmer so I might be wrong).

Sql, how to get data in difference of 24 hours

Select p.uhid,p.inpatientno,dateof admission
from adt.inpatientmaster p
where p.uhid='apd1' and status <>0
Here uhid is unique. I want to check that a patient gets admitted in between 24 hours , here if patient gets admitted again then uhid remains same but inpatientno always change.
Ex:
Registraionno inpatientno dateofadmission
Apd1 xy1 18/01/15
Ap1 ab2 19/01/15
We can do arithmetic on Oracle dates. So yesterday is sysdate - 1.
You need to query the table twice. Once to find the patient records, and once to find any previous matches. Use a self-join to achieve this:
select p1.uhid,
p1.inpatientno as current_inpatientno,
p1.dateofadmission as current_dateofadmission
p2.inpatientno as previous_inpatientno,
p2.dateofadmission as previous_dateofadmission
from adt.inpatientmaster p1
join adt.inpatientmaster p2
on p2.uhid = p1.uhid
where p1.uhid='apd1'
and p1.status <> 0
and p2.dateofadmission >= p1.dateofadmission-1
and p2.inpatientno != p1.inpatientno
/
You may need to restrict on p2.status <> 0 as well: not sure what your business rules are.
This query will return one row for each match. If there are several admissions within the same 24 hours the result set will have one row for each combination.
SELECT p.uhid,
p.inpatientno,
p.dateofadmission
FROM adt.inpatientmaster p
WHERE p.status<>0
AND p.dateofadmission <= p.dateofadmission +1
AND p.uhid='APD1'