Conditional SQL Join using a 3rd table - sql

I've got a problem with an Asset Database that I have been developing for a customer in MSSQL. It entails capturing Required Actions, for example Lifting Equipment at a specific location needs to be inspected 6 months after purchase. The Due Dates for these required actions can be calculated in different ways but to simplify here will be calculated based on their Purchase Date.
So to that end I have a table called tblActionsRequired that contains the following relevant fields:
ActionID - for the action required
EquipmentCategoryID or EquipmentTypeID or EquipmentID - so either one of these fields are required. With this they specify that an action is required for either a category of equipment or an equipment type or a specific piece of equipment. So an example would be that a 2kg Powder Fire Hydrant would be an equipment type, it would fall into the category Fire Safety Equipment and there might be a specific 2kg Powder Fire Hydrant with an asset number of say PFH2KG001.
BasedAtID - the company's branches or sites
Ideally what I'd like to do is keep as much as possible in one query as opposed to creating separate queries or views for every combination and then adding them together using UNIONs. I have several other similar fields by which these required actions can be segmented so it may seem simple enough here to just use unions but I've calculated I would need to cater for 48 different combinations and probably create a View for each and then UNION them together!
So next I have tblEquipment that contains the following relevant keys:
EquipmentID - the primary key
EquipmentTypeID = foreign key, which Equipment Type this asset is a member of
BasedAtID - foreign key, which site the asset is located at
The Equipment Types then belong to Equipment Categories and the Categories then allow building a tree structure with parent-child relationships, but these I think I have sufficiently taken care of in creating a view called vwCategoryTree with the following fields:
ParentCategoryID
EquipmentTypeID
This view has been tested and checks out fine, it cuts through the tree structure and allows you to perform joins between EquipmentTypeID and their ultimate parents with EquipmentCategoryID.
What I need help with is how to do some sort of conditional join between tblActionsRequired and tblEquipment based on which of the fields EquipmentCategoryID, EquipmentTypeID, or EquipmentID have a value. If only EquipmentID or EquipmentTypeID could be specified then I think this would work:
ON (tblActionsRequired.EquipmentID IS NOT NULL AND tblEquipment.EquipmentID = tblActionsRequired.EquipmentID) OR (tblActionsRequired.EquipmentTypeID IS NOT NULL AND tblActionsRequired.EquipmentTypeID = tblEquipment.EquipmentTypeID)
But how do I bring a third table into this join to cater for EquipmentCategoryID or at least avoid having to use a UNION?
Sorry if something doesn't make sense, please just ask! Thank you very much!

One possible approach:
select ...
from tblEquipment e
left join vwCategoryTree c on e.EquipmentTypeID = c.EquipmentTypeID
join tblActionsRequired r
on (e.EquipmentID = r.EquipmentID or
e.EquipmentTypeID = r.EquipmentTypeID or
c.ParentCategoryID = r.EquipmentCategoryID)

Related

Terminology am I doing a one to many or many to many? Left join? Right?

I need to research this but am confused about the terminology of what I should be researching.
Table1 fields:
salenumber
category
quantity
price
Table2 fields:
field
category
requirement
Table3 fields:
field
salenumber
value
I need to combine these.
Essentially one (salenumber, category, quantity, Price) can have a dynamic number of "fields" containing unique "data associated with it. I'm a little confused as to the terminology of what I am doing here. I'm all mixed up with left and right joins and many to one and many to many databases. If I simply knew the term for what I am trying to do it would help me to narrow down my research.
Joins are for your queries - which you run to find specific records from your database. You aren't there yet. First, you want to do table design, which is where you consider many-to-many and one-to-many relationships.
Start by thinking about what the tables represent. For example, a single sale can involve multiple different products (e.g. you buy a fork, a spoon, a knife, and a new car). Each product can be in a different category (utensils or motor vehicles, in this case). In your table design, you would decide whether a product belongs to only a single category or to multiple categories.
Let's assume there's just one category per product - then you can have many products in one category (fork, spoon and knife are all utensils), but a product can have only one category. In this case, Category to Product is a One-to-Many relationship.
How these connect is where the related fields in tables come in to play - so in the Product table you have 'Category', which refers to the Category table ('Fork' is of Category 'Utensil', and in the Category table 'Utensil' is an entry with additional information).
You probably want to look up some basic database lessons to help you out. There are some good free online classes and resources - just search for info about databases.
This may help understand joins: http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
If you have a row in Table_A with some id, and multiple rows in Table_B related referencing that id you can do a LEFT OUTER JOIN (often just LEFT JOIN). This will match the data in Table_A to Table_B - but you will have as many rows as there were in Table_B!
If you want all of the Table_B data related to the Table_A row in one row result you need an aggregate function. In SQL this is normally something like array_agg. When you do this you need to tell it what to GROUP BY, in my example it would be Table_A.id

Building queries in Access 2007?

I'm trying to build a query where I'm able to get names of clients. So I have two tables, the 1st table has a column AppointmentNO, and this field is a number (there are other columns but they're irrelevant). In the 2nd table I have an ID as primary key, FirstName, LastName. ID is what matches the AppointmentNO in the first table.
Basically what I'm trying to do is link the two tables so that when I have an AppointmentNO in one column, I can see the LASTNAME associated with it in the 2nd column (need to include this in my report). I'm trying to link the AppointmentNO to ID and on JOIN PROPERTIES -> include all records from left table (1st table) and only those from right table (2nd table) where the joined fields are equal.
If I try to run the query it gives me a MISMATCH error. What am I doing wrong?
The type mismatch error could be happening:
because the two fields that you're trying to join aren't set as the same data type (e.g. one is Number and the other is Text) - check this in the Properties tabs for the relevant fields in each table;
it could be that Access has a join between the tables involving other fields (it will sometimes do this with AutoIDs) - you can check the relationships (and establish them) in the Tools -> Relationships window (where this is located might depend on your version). You can also use this tool to explicitly build the relationship, by connecting your 'ID' to 'AppointNO' - though you should still ensure that the fields are of the same data type.
ADDITION:
Based on what you're describing, I think this is the situation (correct me if I'm wrong, though):
Three tables - Client, AppointmentNO, Children
In each table, there is a 'MemberID' - this is primary key in Client Table, and is Foreign Key in the other tables.
The Children and AppointmentNO tables are linked to Client table by one-to-many relationships (a client can have >1 children and >1 appointment).
I'd set this up so that the Member ID is the same datatype in each table, and join all tables on that field. Then, when set up a query that gives you MemberID, ClientName, ClientDOB (and anything else you want from the client table), ChildName, and AppointmentID. Once the query is working and giving you the desired output, you can build a report and group the output by Client and Client Description, so you'll get "Client A" followed by list of appointments and children, then "Client B" etc.
Hope that's clear-ish.

Business Objects Universes - joins to restrict data

I'm very new to universe design and would really appreciate a point in the right direction...
I have a table Sales that records details of sales made by staff members. It includes the following fields:
SaleID
StaffID
SaleDate
I also have a table StaffDepts that has the historic department information for each member of staff:
StaffID
DeptName
StartDate
EndDate
I would like to build a universe that pulls through the name of the department in which the member of staff was placed on the date of the sale.
I tried linking the two tables with three joins in Designer (I amended the Expression box in the Edit Join window of each join):
Sales.StaffID = StaffDepts.StaffID
StaffDepts.StartDate <= Sales.SaleDate
(StaffDepts.EndDate IS NULL) OR (StaffDepts.EndDate > Sales.SalesDate)
This failed pretty spectacularly! Can any suggest how I can achieve what I am trying to do?!
Many thanks!
You've created three separate joins in Designer, but I believe you really want them all to work as one. You can (and if I understand what you want to do here, should) create only one join in Designer and include all of the clauses above in it with AND.
I'm assuming the spectacular fail was because by creating three separate joins, you'd introduced loops into your universe. You would only want to create separate joins if you needed the two tables to be joined differently in different contexts -- and in that case, you would create those separate contexts and assign the joins to them accordingly.

Joining same column from same table multiple times

I need a two retrieve data from the same table but divided in different columns.
First table "PRODUCTS" has the following columns:
PROD_ID
PRO_TYPE_ID
PRO_COLOR_ID
PRO_WEIGHT_ID
PRO_PRICE_RANGE_ID
Second table "COUNTRY_TRANSLATIONS" has the following columns:
ATTRIBUTE_ID
ATT_LANGUAGE_ID
ATT_TEXT_ID
Third and last table "TEXT_TRANSLATIONS" has the following columns:
TRANS_TEXT_ID
TRA_TEXT
PRO_TYPE_ID, PRO_COLOR_ID, PRO_WEIGHT_ID and PRO_PRICE_RANGE_ID are all integers and are found back in the column ATTRIBUTE_ID multiple times (depending on howmany translations are available). Then ATT_TEXT_ID is joined with TRANS_TEXT_ID from the TEXT_TRANSLATIONS table.
Basically I need to run a query so I can retreive information from TEXT_TRANSLATIONS multiple times. Right now I get an error saying that the correlation is not unique.
The data is available in more then 20 languages, therefore the need to work with intergers for each of the attributes.
Any suggestion on how I should build up the query? Thank you.
Hopefully, you're on an RDBMS that supports CTEs (pretty much everything except mySQL), or you'll have to modify this to refer to the joined tables each time...
WITH Translations (attribute_id, text)
as (SELECT c.attribute_id, t.tra_text
FROM Country_Translations c
JOIN Text_Translations t
ON t.trans_text_id = c.att_text_id
WHERE c.att_language_id = #languageId)
SELECT Products.prod_id,
Type.text,
Color.text,
Weight.text,
Price_Range.text
FROM Products
JOIN Translations as Type
ON Type.attribute_id = Products.pro_type_id
JOIN Translations as Color
ON Color.attribute_id = Products.pro_color_id
JOIN Translations as Weight
ON Weight.attribute_id = Products.pro_weight_id
JOIN Translations as Price_Range
ON Price_Range.attribute_id = Products.pro_price_range_id
Of course, personally I think the design of the localization table was botched in two ways -
Everything is in the same table (especially without an 'attribute type' column).
The language attribute is in the wrong table.
For 1), this is mostly going to be a problem because you now have to maintain system-wide uniqueness of all attribute values. I can pretty much guarantee that, at some point, you're going to run into 'duplicates'. Also, unless you've designed your ranges with a lot of free space, the data values are non-consecutive for type; if you're not careful there is the potential for update statements being run over the wrong values, simply because the start and end of the given range belong to the same attribute, but not every value in the range.
For 2), this is because a text can't be completely divorced from it's language (and country 'locale'). From what I understand, there are parts of some text that are valid as written in multiple languages, but mean completely different things when read.
You'd likely be better off storing your localizations in something similar to this (only one table shown here, the rest are an exercise for the reader):
Color
=========
color_id -- autoincrement
cyan -- smallint
yellow -- smallint
magenta -- smallint
key -- smallint
-- assuming CYMK palette, add other required attributes
Color_Localization
===================
color_localization_id -- autoincrement, but optional:
-- the tuple (color_id, locale_id) should be unique
color_id -- fk reference to Color.color_id
locale_id -- fk reference to locale table.
-- Technically this is also country dependent,
-- but you can start off with just language
color_name -- localized text
This should make it so that all attributes have their own set of ids, and tie the localized text to what it was localized to directly.

SQL join basic questions

When I have to select a number of fields from different tables:
do I always need to join tables?
which tables do I need to join?
which fields do I have to use for the join/s?
do the joins effects reflect on fields specified in select clause or on where conditions?
Thanks in advance.
Think about joins as a way of creating a new table (just for the purposes of running the query) with data from several different sources. Absent a specific example to work with, let's imagine we have a database of cars which includes these two tables:
CREATE TABLE car (plate_number CHAR(8),
state_code CHAR(2),
make VARCHAR(128),
model VARCHAR(128),);
CREATE TABLE state (state_code CHAR(2),
state_name VARCHAR(128));
If you wanted, say, to get a list of the license plates of all the Hondas in the database, that information is already contained in the car table. You can simply SELECT * FROM car WHERE make='Honda';
Similarly, if you wanted a list of all the states beginning with "A" you can SELECT * FROM state WHERE state_name LIKE 'A%';
In either case, since you already have a table with the information you want, there's no need for a join.
You may even want a list of cars with Colorado plates, but if you already know that "CO" is the state code for Colorado you can SELECT * FROM car WHERE state_code='CO'; Once again, the information you need is all in one place, so there is no need for a join.
But suppose you want a list of Hondas including the name of the state where they're registered. That information is not already contained within a table in your database. You will need to "create" one via a join:
car INNER JOIN state ON (car.state_code = state.state_code)
Note that I've said absolutely nothing about what we're SELECTing. That's a separate question entirely. We also haven't applied a WHERE clause limiting what rows are included in the results. That too is a separate question. The only thing we're addressing with the join is getting data from two tables together. We now, in effect, have a new table called car INNER JOIN state with each row from car joined to each row in state that has the same state_code.
Now, from this new "table" we can apply some conditions and select some specific fields:
SELECT plate_number, make, model, state_name
FROM car
INNER JOIN state ON (car.state_code = state.state_code)
WHERE make = 'Honda'
So, to answer your questions more directly, do you always need to join tables? Yes, if you intend to select data from both of them. You cannot select fields from car that are not in the car table. You must first join in the other tables you need.
Which tables do you need to join? Whichever tables contain the data you're interested in querying.
Which fields do you have to use? Whichever fields are relevant. In this case, the relationship between cars and states is through the state_code field in both table. I could just as easily have written
car INNER JOIN state ON (state.state_code = car.plate_number)
This would, for each car, show any states whose abbreviations happen to match the car's license plate number. This is, of course, nonsensical and likely to find no results, but as far as your database is concerned it's perfectly valid. Only you know that state_code is what's relevant.
And does the join affect SELECTed fields or WHERE conditions? Not really. You can still select whatever fields you want and you can still limit the results to whichever rows you want. There are two caveats.
First, if you have the same column name in both tables (e.g., state_code) you cannot select it without clarifying which table you want it from. In this case I might write SELECT car.state_code ...
Second, when you're using an INNER JOIN (or on many database engines just a JOIN), only rows where your join conditions are met will be returned. So in my nonsensical example of looking for a state code that matches a car's license plate, there probably won't be any states that match. No rows will be returned. So while you can still use the WHERE clause however you'd like, if you have an INNER JOIN your results may already be limited by that condition.
Very broad question, i would suggest doing some reading on it first but in summary:
1. joins can make life much easier and queries faster, in a nut shell try to
2. the ones with the data you are looking for
3. a field that is in both tables and generally is unique in at least one
4. yes, essentially you are createing one larger table with joins. if there are two fields with the same name, you will need to reference them by table name.columnname
do I always need to join tables?
No - you could perform multiple selects if you wished
which tables do I need to join?
Any that you want the data from (and need to be related to each other)
which fields do I have to use for the
join/s?
Any that are the same in any tables within the join (usually primary key)
do the joins effects reflect on fields specified in select clause or on where conditions?
No, however outerjoins can cause problems
(1) what else but tables would you want to join in mySQL?
(2) those from which you want to correlate and retrieve fields (=data)
(3) best use indexed fields (unique identifiers) to join as this is fast. e.g. join
retrieve user-email and all the users comments in a 2 table db
(with tables: tableA=user_settings, tableB=comments) and both having the column uid to indetify the user by
select * from user_settings as uset join comments as c on uset.uid = c.uid where uset.email = "test#stackoverflow.com";
(4) both...