Make the correct INNER JOIN when using a BETWEEN - sql

I'm sorry if this is a dumb question, but I have this particular case I can't figure how to handle. I need a query where I get all date values between two date values on another tables, and right now this is my query
SELECT h.hour_gkey, h.hour_time
FROM Hours as h
INNER JOIN ServiceHours sh ON h.hour_gkey BETWEEN sh.openhour_hour_gkey AND sh.closehour_hour_gkey;
So to explain it a bit further, the ServiceHours table has two fields openhour_hour_gkey and closehour_hourg_key that are integer, this two fields contain Foreign Keys of the Hours table and therefore they have time values, the hour_gkey(integer) its the primary key of Hours table and I need to show only the values of hour_time (date fieldtype) that are between the dates that correspond to those two fields. How could I do that
Using right now SQL Server 2014

I'm interpreting your question to be "How do I select all of the rows of Hours whose hour_time values are between those related to ServiceHours.openhour_hour_gkey and ServiceHours.closehour_hour_gkey?"
I furthermore suppose that it is intentional that you are neither selecting any columns from ServiceHours nor filtering to narrow the results to those associated with a single ServiceHours row. Thus, if there are multiple ServiceHour rows you will get a set of Hours rows for each one, with these sets not necessarily being disjoint, and with no indication of which goes with which ServiceHour.
In any case, you need to perform a join for each relationship you want to traverse, and for this query you seem to want another join to get the target data. that might look like this:
SELECT h.hour_gkey, h.hour_time
FROM
Hours h
CROSS JOIN ServiceHours sh
INNER JOIN Hours sho
ON sh.openhour_hour_gkey = sho.hour_gkey
INNER JOIN Hours shc
ON sh.closehour_hour_gkey = shc.hour_gkey
WHERE h.hour_time BETWEEN sho.hour_time AND shc.hour_time;
I have written the BETWEEN condition as a filter predicate instead of a join predicate because that seems a better characterization. For inner joins, however, the two alternatives are equivalent. Note also that this query is semantically equivalent to #DaveCosta's.

Here is one way I think it could be done. I am not certain if the syntax is exactly right for SQL Server. But the basic idea is, you would need to join from ServiceHours to Hours to get the actual open/close hour values, then select the rows from Hours with values in that range.
WITH min_max AS (
SELECT h1.hour_time min_hour_time, h2.hour_time max_hour_time
FROM ServiceHours sh
JOIN Hours h1 ON h1.hour_gkey = sh.openhour_hour_gkey
JOIN Hours h2 ON h2.hour_gkey = sh.closehour_hour_gkey
)
SELECT h.hour_time
FROM Hours h
JOIN min_max ON h.hour_time BETWEEN min_max.min_hour_time AND min_max.max_hour_time
(Note I'm assuming that ServiceHours has only one row. If it doesn't, there is probably some other field you want to include in both the subquery and the main query to indicate which row in ServiceHours each resulting row relates to.)

Related

Oracle Left outer joining same tables multiple times

I have four tables named as ROLE,EMPLOYEE, HIERARCHY_UNIT, EMPLOYEE_CLASS all of them have a different nameid column which is a primary key to STRING_TABLE table.Also STRING_TABLE will have a column stringtext that stores the exact text or we can say name.
All of these tables are linked as all of them will have a empid column.
Now i want to select some information from EMPLOYEE table along with the names of corresponding role, hierarchy_unit,employeeclass.My select query willl be something like
SELECT EMPST.STRINGTEXT,EROLE.STRINGTEXT,EHIER.STRINGTEXT,EEMPCLASS.STRINGTEXT
FROM EMPLOYEE EMP
INNER JOIN ROLE RO ON ROLE.EMPID=EMP.EMPID
INNER JOIN EMPLOYEE_CLASS EC ON EC.EMPID = EMP.EMPID
INNER JOIN HIERARCHY_UNIT HU ON HU.EMPID=EMP.EMPID
LEFT OUTER JOIN STRING_TABLE EMPST ON EMPST.NAMEID=EMP.NAMEID
LEFT OUTER JOIN STRING_TABLE EROLE ON RO.NAMEID=EROLE.NAMEID
LEFT OUTER JOIN STRING_TABLE EHIER ON HU.NAMEID=EHIER.NAMEID
LEFT OUTER JOIN STRING_TABLE EEMPCLASS ON EEMPCLASS.NAMEID=EC.NAMEID
The above query is working fine but i have a question whether doing the join to same table will not cause any performance issue. In the above example i have taken left outer join 3 times ( in actual i have a case of 26 joins with the string table ).Is there any way to optimize the above select query n and not to take join with same table multiple times?
It is hard to say anything about performance of a query without it's execution plan. Guessing is the only thing possible at the moment.
So, assuming STRING_TABLE.NAMEID is worth indexing (there are many unique values) and you already have this column indexed, this query is fine.
In the select-part of the query you've specified 4 STRING_TABLE's columns. This means you're asking the database to find values for different NAMEIDs from 4 different lines in that table and post them in one line for each line in the result query.
What the database has to do is looking 4 times (or 26 in production) for the particular line with particular nameid for each line in the output. This is why you need to join STRINGS_TABLE 4 times and, again, this is fine as long as column is worth-indexing and is already indexed.
If there are many non-unique data in NAMEID column, you might need to use partitioning or even to change database structure in order to get better performance. But, again, query is fine and I don't see any way to make it better

Oracle Different row counts using Join and without Join

I have an Oracle DB and use this query below to fetch records for a requirement. Five columns from three tables and a where condition.
select un.name, he.emp_no, he.lname, hr.in_unit, hr.out_unit
from hr_employee he
inner join hr_roster hr on he.eid = hr.eid
inner join units un on he.unit = un.unit_code
where hr.unit_date = to_date( '24-JUL-20','dd-MON-yy')
Later on I realize that if used in this way below, without Joins it is slightly faster.
select un.name, he.emp_no, he.lname, hr.in_unit, hr.out_unit
from hr_employee he, hr_roster hr, units un
where hr.unit_date = to_date( '24-JUL-20','dd-MON-yy')
But I notice that there's a difference of the rows getting fetched comparing the queries above.
When I took a row count of both queries, the one using Joins returns 1012 and the other one keeps fetching without a count.
I am bit confused and do not know which query is the most suitable to use.
The Second query treats as a CROSS JOIN, since there's no respective join conditions among those tables' columns, just exists a restriction due to a certain date, while the first one has a standard inner joins among tables with regular INNER JOIN conditions.
The second query is basically incorrect as does not have join conditions on the second and 3rd table, except for a limitation on a date for the first table only. So it basically produces a cartesian product of the selected records from 1rst table times ALL records on 2nd table times ALL records on 3rd table.
The first query, which looks more correct, produces the selected records on 1rst table times the records on 2nd table joined by he.eid = hr.eid times the records on 3rd table joined by he.unit = un.unit_code

Newbie to SQL I have run the the inner join query but result comes up with columns only

I have run this query in adventureworks but the result is run successfully but i only get the columns instead of the data with columns how so?
select
a.BusinessEntityID,b.bonus,b.SalesLastYear
from
[Sales].[SalesPersonQuotaHistory] a
inner join
[Sales].[SalesPerson] b
on
a.SalesQuota = b.SalesQuota
My best guess is that instead of joining the tables on SalesQuota, you should be joining them on something else - An ID field, typically.
I don't have Adventureworks here, but judging from the names of the tables and the columns that you've provided, I would assume that there's a SalesPersonID field of some sort that actually connects a Salesperson's quota history to the Salesperson him/herself.
I would expect that you're looking for something closer to this:
SELECT
a.BusinessEntityID
,b.bonus
,b.SalesLastYear
FROM [Sales].[SalesPersonQuotaHistory] a
INNER JOIN [Sales].[SalesPerson] b
ON a.SalesPersonID = b.SalesPersonID
General Knowledge:
INNER JOIN means "Show me only entries (rows) that have a matching value on both sides of the condition." (i.e. The value in Table A matches the value in Table B).
So ON a.SalesQuota = b.SalesQuota means "Only where the value of SalesQuota in Table A matches the value of SalesQuota in Table B."
I'm not sure what the purpose of this query could be, since it is entirely possible that two salespeople have the same values in both tables, and then you would get duplicate rows (because the values of SalesQuota would match in both cases), or that the values wouldn't match at all, and then you wouldn't get any rows - I suspect that is what's happening to you.
Consider the conditions of what you're trying to join. Are you really trying to join quota amounts, or are you trying to retrieve quota information for specific salespeople? The answer should help guide your JOIN conditions.

How to join more than one column between 2 tables

I am currently having trouble with learning SQL, and am unable to get a table to join to another one when two or more of the columns in both tables are the same.
For example, I have 2 tables:
(I'm not sure how to post the code so I've just posted a link I hope that this is ok)
This is table 1, it shows how long each stage of each Project will take
http://puu.sh/gt92M/3dfe0063f0.png
This is table 2, it shows how long the stage of each project has been worked upon
http://puu.sh/gt9HO/2fd5090c9a.png
So far I have been able to put them into the same table, but I am unable to get the hours taken into its own column, currently they mix with the hours needed column.
SELECT ID, Stage, SUM(Hours_Taken)
FROM Work
GROUP BY ID, Stage
UNION
SELECT ID, Stage, Hours
FROM Budget_Allocation
GROUP BY ID, Stage
As you can see, each project has stages, and each stage needs a different amount of work hours. I want to be able to display a 4 columned table:
ID
Stage
Hours
Hours_Taken.
You are asking for a result whose columns include some derived from one table and others derived from a different table. That means you need to perform some kind of JOIN. The UNION operator does not join tables, it just collates multiple row sets into a single row set, eliminating duplicates.
One of the rowsets you want to select from is not a base table, however, but rather the result of an aggregate query. This calls for a subquery, the results of which you join to the other base table as needed:
SELECT
tw.ID AS ID,
tw.Stage AS Stage,
ba.Hours AS Hours,
tw.Hours_Taken AS Hours_Taken
FROM
Budget_Allocation ba
-- JOIN operator --
JOIN (
-- here's the subquery --
SELECT ID, Stage, SUM(Hours_Taken) AS Hours_Taken
FROM Work
GROUP BY ID, Stage
) tw
-- predicate for the preceding JOIN operator --
ON ba.ID = tw.ID AND ba.Stage = tw.Stage
Note that in this case you do not want to join base tables first and then aggregate rows of the joint results, because you are selecting values from one column (Budget_Allocation.Hours) that is neither a grouping column nor a function of the groups. There are workarounds and implementation-specific exceptions to that limitation, but in this case it's easy to do the right thing straight off by aggregating before joining.
you are doing union instead of join.
select w.id,w.stage,w.hours_taken, b.hours
from work w, budge_allocation b
where w.id = b.id and
w.stage = b.stage;
now you have everything you need in one row and can do what you want with it.

Outer join on multiple tables on SQL Server

I'm writing a recursive algorithm. It's taking data from 4 periods in the last year, and creating a resultset.
The issue is that not all scenarios return 4 periods.
So, I've done an set of 4 selects on the table, used an outer join to connect them. Their joined on the PK. However, they're all joined to the first datapoint. Sometimes this datapoint doesn't exist, which throws a wrench in my join.
Is there an easy way to do a full outer join on 4 tables using a PK with doing 16 where clauses and outer joining them with (+)
Actually, does (+) even work on sql server?
Thanks,
Eric
You should first create a complete dataset containing all periods in the previous year. *You could do this by using something like SELECT DISTINCT PERIOD FROM (SELECT PERIOD FROM SetA union SELECT PERIOD FROM SetB UNION SELECT PERIOD FROM SETC etc...) AS COMPLETESET"
Then left join against the COMPLETESET all other datasets on period.
The data points that do not exist in the joins will return null values.