I have been trying to learn SQL using SQLBolts tutorial and for this exercise, I needed to write a query that showed the names of all the buildings with no employees using only LEFT JOIN. I had an answer different from the website and I am wondering why they both work. The only difference between our solutions is I put WHERE buildings is NULL vs SQL Bolts solution of WHERE role is null. If building value is null shouldn't that return a null value? Also same with role how does the Database know a building has a null value for role when there isn't a building attached to that role?
my query
SELECT building_name
FROM buildings
LEFT JOIN EMPLOYEES
ON buildings.building_name = EMPLOYEES.building
WHERE building IS NULL
SQL BOLT query
SELECT DISTINCT building_name, role
FROM buildings
LEFT JOIN employees
ON building_name = building
WHERE Role IS NULL
Buildings (table1) and Employees (table2)
Null Values from Database
Formally both queries are incorrect until complete tables structures are defined. When the query datasource includes more than one table copy then each column name must be specified with its table alias part. Exclusion - the column names used in USING clause or common columns when NATURAL JOIN is used, these columns may be used without table aliases. Backward exclusion - the query is used in compound statement, and local variables are present, in this case aliases must be used unconditionally.
If the tables structures claims that the queries texts are correct (all columns which have no table aliases are unique) then your query is more correct because it uses the column used in JOIN condition while testing for NULL. The "SQL BOLT query"may give wrong result if Role column is nullable and some rows contains NULL in this column.
If Role is defined as NOT NULL (directly or indirectly - for example, by according CHECK constraint, or it is a part of primary key) then both queries will give the same output.
Related
I have a couple of tables in a mySQL database. For simplicity I'll just show some basic fields:
Table: sources:
sourceID int not null unique primary key
trigger int not null
<other stuff>
Table: sourceBS
id not null unique primary key
sourceID int not null,
name varchar(20),
SourceID in the in the sourceBS table is a foreign key referencing its namesake in sources, with the cascade option. I have tested this: if I delete an entry in sources, the corresponding entry in sourceBS also vanishes. Good.
I want to select some stuff from a join of sources and sourceBS, filtering based on a "sources" property. This should be easy, via a join which, I think, the foreign key should render pretty efficient, so:
SELECT sources.sourceID, sourceBS.*
FROM sources
LEFT JOIN sourceBS ON sources.sourceID = sourceBS.sourceID
WHERE trigger=1;
But when this runs, each row has "NULL" for the values returned from sourceBS, even sourceBS contains entries matching the condition. I can verify this:
SELECT *
FROM sourceBS
WHERE sourceID IN (
SELECT sourceID
FROM sources
WHERE trigger=1
);
Here I get a proper set of results, i.e. non-null values. But, while this works as a proof of concept, it's no good in real life because I want to return a bunch of stuff from the "sources" table as well, and I don't want to have to run multiple queries in order to get what I want.
Returning to the join, if I replace the left join with an inner join, then no results are returned. It is as if, somehow, the "join" is simply not finding any matches in the sourceBS table, and yet they are there as the second query shows.
Why is this happening? I know that this join has a 1:M relationship, sourceBS could have multiple entries for a given entry in sources, but that should be OK. I can test exactly this type of join on other DBs, and it works.
OK, so I've solved this - it wasn't a transaction issue in the end:when I tried it on the original machine, it failed again. It was the order of the join. It appears that in my terminal I had the "ON" clause the other way round to above, that is, I was doing:
... LEFT JOIN sourceBS ON (sourceBS.blockSourceID=sources.sourceID)
which returns all the nulls. If I do it (as in the above code I pasted)
... LEFT JOIN sourceBS ON (sources.sourceID=sourceBS.sourceID
it works. When I tried it the second time last night on a new machine, I'd used the second formulation.
Guess I'd better read up on joins to understand why this happened!
I'm doing a beginners course on SQL and databases, and I'm trying to write a select statement that will join two tables on Oracle Apex. I'm using join...on... to do so. The two tables each have a column named the same, with the columns filled with matching values. Obviously, they tables need to join where the values match in each column. Here's the code:
select TRANSACTION_ID, BUYER_ID, FIRST_NAME
from TRANSACTIONS
join BUYER
on TRANSACTIONS.BUYER_ID = BUYER.BUYER_ID;
The two Tables are named "TRANSACTIONS" and "BUYER".
Both tables have a column named "BUYER_ID".
Only the table "TRANSACTIONS" has a column named "LOT_ID", which is associated with the buyer ID
Only the table "BUYER" has a column named "FIRST_NAME", which is associated with the buyer ID
So if I were to simply write,
on BUYER_ID = BUYER_ID;
as I understand that would return the error saying "Columns Ambiguously Defined". But even when I specify the tables, as in,
on TRANSACTIONS.BUYER_ID = BUYER.BUYER_ID;
I still get a "Columns Ambiguously Defined". I wondered if it was an issue with the two columns having to be presented with the same name, so I tried,
on TRANSACTIONS.BUYER_ID as "BUYER_ID1" = BUYER.BUYER_ID as "BUYER_ID2";
but then that returns "invalid relational operator".
Any ideas?
The problem isn't in the join, you also have to fully specify the column in the SELECT statement as well, or it won't know which of the BUYER_ID columns to display. Change it to SELECT BUYER.BUYER_ID and it will work.
Update 4/25/13 6:25AM: I am using MyISAM
I have searched a lot and am not sure the best way to do this. I have two tables that have matching values in different columns and need to return all that apply to where clause.
Table 1 name agent
Relevant Column Names agent_name and team
Table 2 name poll_data
Relevant Column Names agent and duid
So I want to count how many poll results I get from each teambut I need to somehow add the team from agent table to poll_data by matching the agent.agent_name to poll_data.name so I can return only data for that team. How can I match the records and then search them in a single query.
try this ...
$query1="SELECT COUNT(*) FROM poll_data JOIN agent ON (poll_data.agent = agent.agent_name) GROUP BY agent.team";
you should normalize the database using foreign key.
Here is my Database structure (basic relations):
I'm attempting to formulate a one-line query that will populate the clients_ID, Job_id, tech_id, & Part_id and return back all the work orders present. Nothing more nothing less.
Thus far I've struggled to generate this Query:
SELECT cli.client_name, tech.tech_name, job.Job_Name, w.wo_id, w.time_started, w.part_id, w.job_id, w.tech_id, w.clients_id, part.Part_name
FROM work_orders as w, technicians as tech, clients as cli, job_types as job, parts_list as part
LEFT JOIN technicians as techy ON tech_id = techy.tech_name
LEFT JOIN parts_list party ON part.part_id = party.Part_Name
LEFT JOIN job_types joby ON job_id = joby.Job_Name
LEFT JOIN clients cliy ON clients_id = cliy.client_name
Apparently, once all the joining happens it does not even populate the correct foreign key values according to their reference.
[some values came out as the actual foreign key id, not even
corresponding value.]
It just goes on about 20-30 times depending on largest row of a table that I have (one of the above).
I only have two work orders created, So ideally it should return just TWO Records, and columns, and fields with correct information. What could I be doing wrong? Haven't been with MySQL too long but am learning as much as I can.
Your join conditions are wrong. Join on tech_id = tech_id, not tech_id = tech_name. Looks like you do this for all your joins, so they all need to be fixed.
I really don't follow the text of your question, so I am basing my answer solely on your query.
Edit
Replying to your comment here. You said you want to "load up" the tech name column. I assume you mean you want tech name to be part of your result set.
The SELECT part of the query is what determines the columns that are in the result set. As long as the table where the column lives is referenced in the FROM/JOIN clauses, you can SELECT any column from that table.
Think of a JOIN statement as a way to "look up" a value in one table based on a value in another table. This is a very simplified definition, but it's a good way to start thinking about it. You want tech name in your result set, so you look it up in the Technicians table, which is where it lives. However, you want to look it up by a value that you have in the Work Orders table. The key (which is actually called a foreign key) that you have in the Work Orders table that relates it to the Technicians table is the tech_id. You use the tech_id to look up the related row in the Technicians table, and by doing so can include any column in that table in your result set.
I have three tables: students, interests, and interest_lookup.
Students has the cols student_id and name.
Interests has the cols interest_id and interest_name.
Interest_lookup has the cols student_id and interest_id.
To find out what interests a student has I do
select interests.interest_name from `students`
inner join `interest_lookup`
on interest_lookup.student_id = students.student_id
inner join `interests`
on interests.interest_id = interest_lookup.interest_id
What I want to do is get a result set like
student_id | students.name | interest_a | interest_b | ...
where the column name 'interest_a' is a value in interests.name and
the interest_ columns are 0 or 1 such that the value is 1 when
there is a record in interest_lookup for the given
student_id and interest_id and 0 when there is not.
Each entry in the interests table must appear as a column name.
I can do this with subselects (which is super slow) or by making a bunch of joins, but both of these really require that I first select all the records from interests and write out a dynamic query.
You're doing an operation called a pivot. #Slider345 linked to (prior to editing his answer) another SO post about doing it in Microsoft SQL Server. Microsoft has its own special syntax to do this, but MySQL does not.
You can do something like this:
SELECT s.student_id, s.name,
SUM(i.name = 'a') AS interest_a,
SUM(i.name = 'b') AS interest_b,
SUM(i.name = 'c') AS interest_c
FROM students s
INNER JOIN interest_lookup l USING (student_id)
INNER JOIN interests i USING (interest_id)
GROUP BY s.student_id;
What you cannot do, in MySQL or Microsoft or anything else, is automatically populate columns so that the presence of data expands the number of columns.
Columns of an SQL query must be fixed and hard-coded at the time you prepare the query.
If you don't know the list of interests at the time you code the query, or you need it to adapt to changing lists of interest, you'll have to fetch the interests as rows and post-process these rows in your application.
What your trying to do sounds like a pivot.
Most solutions seem to revolve around one of the following approaches:
Creating a dynamic query, as in Is there a way to pivot rows to columns in MySQL without using CASE?
Selecting all the attribute columns, as in How to pivot a MySQL entity-attribute-value schema
Or, identifying the columns and using either a CASE statement or a user defined function as in pivot in mysql queries
I don't think this is possible. Actually I think this is just a matter of data representatioin. I would try to use a component to display the data that would allow me to pivot the data (for instance, the same way you do on excel, open office's calc, etc).
To take it one step further, you should think again why you need this and probably try to solve it in the application not in the database.
I know this doesn't help much but it's the best I can think of :(