1C:Professional training course. Lesson 3-4

Lesson 3-4

LEFT and RIGHT JOINs

INNER JOIN filters out records that don’t meet the ON conditions from both - left and right - tables of the JOIN. If there is a record in the left table having no match in the right table, it will be filtered out. And other way around - a record from the right table having no match in the left table will be filtered out too. In this respect, INNER JOIN is symmetrical. You can write

SELECT ... FROM Table1 INNER JOIN Table2 ON ...

or

SELECT … FROM Table2 INNER JOIN Table2 ON ...

and the result will be exactly the same (if you use the same conditions, of course). Therefore, there is no such thing as LEFT or RIGHT INNER JOIN. Please, also note that INNER JOIN is a default type of JOIN, so you can omit the keyword INNER.

This is not the case for OUTER JOINs. Say, you have these two Catalogs:


The query

SELECT C1.Field1, C2.Field1
FROM Catalog.Catalog1 AS C1
	LEFT JOIN Catalog.Catalog2 AS C2
	ON C1.Field1 = C2.Field1

will return this recordset:


So, all records from the left table have made it into the query result. For those of them who haven’t found a match, the query simply put NULL in the correspondent column.

What happens when we swap the tables around leaving the same type of JOIN? This query

SELECT C1.Field1, C2.Field1
FROM Catalog.Catalog2 AS C2
	LEFT JOIN Catalog.Catalog1 AS C1
	ON C1.Field1 = C2.Field1

will return the different result:


How come we’ve lost one of the records? Well, this time query selects all records from the Catalog2 table and looks for matching pair in the Catalog1 table. Therefore, we cannot see 4-NULL record anymore. It wasn’t in the Catalog2 and didn’t make it to the resulting recordset.

So. For OUTER JOIN, there is no symmetry. Depending on the place of the tables (to the left or right side of the JOIN) the result can be different. This is what we need LEFT and RIGHT keywords in queries for. It defines do we get all records from this table or only matching once.

LEFT OUTER JOIN tells to a query to select records from the left table and then look for matching records in the right table. RIGHT OUTER JOIN tells to a query to select records from the right table and then look for matching records in the left table.

Note, that you can omit the OUTER keyword.

Needless to say, that LEFT and RIGHT keywords are completely interchangeable if you swap the tables at the same time. In other words, Table1 LEFT JOIN Table will return exactly the same result as Table2 RIGHT JOIN Table1.

JOINs’ types resume

Let’s summarize all we have known about JOINs by now.


Multiple JOINs

You can use as many joins as you need in a single query. Say, we can join both MaterialsAndServices and Warehouses catalogs to the Balance table query like this:

SELECT * FROM
	AccumulationRegister.BalanceOfMaterials.Balance AS Balance
	LEFT JOIN Catalog.Warehouses AS Warehouses
ON Balance.Warehouse = Warehouses.Ref
	LEFT JOIN Catalog.MaterialsAndServices AS Materials
ON Balance.Material = Materials.Ref

Or we can select all warehouse, join them to the tabular section MaterialsAndServices of the Services document and then join the MaterialsAndServices catalog:

SELECT * FROM
	Catalog.Warehouses AS Warehouses
	LEFT JOIN Document.Services.MaterialsAndServices AS Material
		ON (Material.Warehouse = Warehouses.Ref)
	LEFT JOIN Catalog.MaterialsAndServices AS MaterialsAndServices
		ON (Material.MaterialOrService = MaterialsAndServices.Ref)

WHERE and JOIN conditions

Say, we need to select all materials from MaterialsAndServices tabular section of Services documents. If we write the query like this

SELECT * FROM
Document.Services.MaterialsAndServices AS ServicesMaterialsAndServices
LEFT JOIN Catalog.MaterialsAndServices AS MaterialsAndServices
	ON ServicesMaterialsAndServices.MaterialOrService = MaterialsAndServices.Ref

we will get all tabular section records, including services we don’t need. So we need to add a condition MaterialsAndServices.MaterialOrService = VALUE(Enum.MaterialOrService.Material).

VALUE keyword is used to obtain a predefined catalog item or enumeration item and use it directly in the query text. Another option which is available here - is using a parameter (&<parameter name>) and setting its value with Query.SetParameter(<parameter name>, <parameter value>).

One more question is where we need to add this condition to? Into JOIN conditions or into WHERE conditions?

Let’s check it out.


Here is the texts of the queries we just used. Query with WHERE filter:

SELECT
	MaterialsAndServices.Description,
	ServicesMaterialsAndServices.Quantity
FROM
	Document.Services.MaterialsAndServices AS ServicesMaterialsAndServices
	LEFT JOIN Catalog.MaterialsAndServices AS MaterialsAndServices
	ON ServicesMaterialsAndServices.MaterialOrService = MaterialsAndServices.Ref 
WHERE
	MaterialsAndServices.MaterialOrService = VALUE(Enum.MaterialOrService.Material

Query with INNER JOIN filter

SELECT
	MaterialsAndServices.Description,
	ServicesMaterialsAndServices.Quantity
FROM
	Document.Services.MaterialsAndServices AS ServicesMaterialsAndServices
	INNER JOIN Catalog.MaterialsAndServices AS MaterialsAndServices
ON ServicesMaterialsAndServices.MaterialOrService = MaterialsAndServices.Ref AND
	MaterialsAndServices.MaterialOrService = VALUE(Enum.MaterialOrService.Material

One more question to answer: if WHERE and INNER JOIN conditions do the same filtering job, which one of them is better to use? Well, in most cases there will be no real difference performance-wise, but we still recommend you to use JOIN when you filter one table against another. It’s a more natural choice and there is a possibility of speeding-up your application in some cases.

Hidden JOINs in 1C query syntax

Let’s take another look at our last query.


This is the query text we used in the video above:

SELECT
	ServicesMaterialsAndServices.Quantity,
	ServicesMaterialsAndServices.MaterialOrService.Description,
	ServicesMaterialsAndServices.MaterialOrService.Parent.Description
FROM
	Document.Services.MaterialsAndServices AS ServicesMaterialsAndServices

So, how does the Platform gets this fields’ values from other tables using no JOINs? Well, it’s simple: the Platform, actually, uses JOINs, it just doesn’t show them to us. Before the query will be run on the DBMS, the Platform will convert it into a pure SQL form, getting rid of everything that cannot be understood by the DBMS (replacing the 1C metadata objects’ names with DBMS table names and so on). During this procedure the Platform will replace these dots with LEFT OUTER JOINS to the correspondent tables.

So, you can consider this dot syntax to be 1C query language extension to the classic SQL. It doesn’t introduce any new functionality - it just allows you to shorten you query text and make it more transparent.

Note, that there is no difference between dot and classic JOIN syntax whatsoever. The query above will be executed exactly in the same way as the following query:

SELECT
	ServicesMaterialsAndServices.Quantity,
	MaterialsAndServices.Description,
	MaterialsAndServices1.Description AS Description1
FROM
	Document.Services.MaterialsAndServices AS ServicesMaterialsAndServices
		LEFT JOIN Catalog.MaterialsAndServices AS MaterialsAndServices
		ON ServicesMaterialsAndServices.MaterialOrService = MaterialsAndServices.Ref
		LEFT JOIN Catalog.MaterialsAndServices AS MaterialsAndServices1
		ON MaterialsAndServices1.Ref = MaterialsAndServices.Parent

Note, that in the query above we used Catalog.MaterialsAndServices twice - first time to get the Description of the material or service and second time - to get the Description of the folder it sits in.

So, we can use the same metadata object as many times as we need.

Checking the balance using a query with JOIN

Now we are ready to write the checking balance query using a JOIN filtering.

We have two tables to get data from:

  • AccumulationRegister.BalanceOfMaterials.Balance AS BalanceTable
  • Document.Services.MaterialsAndServices AS TabularSection

And five conditions to check:

  • TabularSection.Ref = &Ref
  • TabularSection.MaterialOrService = &Material
  • BalanceTable.QuantityBalance < 0
  • BalanceTable.Material = TabularSection.MaterialOrService
  • BalanceTable.Warehouse = TabularSection.Warehouse

What JOIN should we use - LEFT or RIGHT? In what order should we list the tables? BalanceTable JOIN TabularSection or TabularSection JOIN BalanceTable? How we use the conditions - what conditions in the WHERE clause and what conditions in the ON section of JOIN?

Let’s use the following data set as an example and consider the step-by-step approach to build the query we need.


Here is a basic algorithm to follow:

1. Choosing a main table

Let’s call a “main” the table in the FROM section of a query. Of course, from SQL syntax viewpoint there is no such thing as a main table in a query. We can write “BalanceTable JOIN TabularSection” query or “TabularSection JOIN BalanceTable” and if we do everything right, the result will be the same in every sense.

So, choosing the main table is just a matter of convenience. On my liking, the TabularSection table is more meaningful in this role then the BalanceTable, so I will be using it.

The first step on the way to the resulting query is this:

SELECT
	TabularSection.Warehouse AS Warehouse,
	TabularSection.MaterialOrService AS Material
FROM
	Document.Services.MaterialsAndServices AS TabularSection

The result will be the following:


2. Filtering the main table against its independent conditions

If you take another look at the conditions list, you will see that there are conditions of two different types:

  • Independent - that filter the table against its own fields’ values:
    • TabularSection.Ref = &Ref
    • TabularSection.MaterialOrService = &Material
    • BalanceTable.QuantityBalance < 0
  • Dependent - that filter one table against another table fields’ values
    • BalanceTable.Material = TabularSection.MaterialOrService
    • BalanceTable.Warehouse = TabularSection.Warehouse

On this step we need to add all main table’s independent conditions to the WHERE clause:

SELECT
	TabularSection.Warehouse AS Warehouse,
	TabularSection.MaterialOrService AS Material
FROM	
Document.Services.MaterialsAndServices AS TabularSection
WHERE
	TabularSection.Ref = &Ref 
	AND TabularSection.MaterialOrService = &Material 

And this would be the result:


3. Choosing the JOIN type

We use <main table> LEFT JOIN to look up for additional information we don’t have in the main table. We use <main table> INNER JOIN to filter the main table against joined table.

Therefore, we need INNER JOIN here.

4. Using main table’s dependent conditions in JOIN’s ON clause

Let’s add our INNER JOIN and specify all dependent conditions in its ON section:

SELECT
	TabularSection.Warehouse AS Warehouse,
	TabularSection.MaterialOrService AS Material,
	BalanceTable.QuantityBalance AS QuantityBalance
FROM
	Document.Services.MaterialsAndServices AS TabularSection
		INNER JOIN AccumulationRegister.BalanceOfMaterials.Balance AS BalanceTable
		ON (BalanceTable.Material = TabularSection.MaterialOrService)
			AND (BalanceTable.Warehouse = TabularSection.Warehouse)
WHERE
	TabularSection.Ref = &Ref
	AND TabularSection.MaterialOrService = &Material

Note, that now (after we added the JOIN) we can use the joined table fields in the SELECT list, so we added BalanceTable.QuantityBalance there. Also note, that when we join multiple tables, we should use correspondent conditions in every ON clause like this:

SELECT… FROM <main table>
JOIN <table1> ON <table1 dependent conditions>
JOIN <table2> ON <table2 dependent conditions>
...

This is the resulting recordset we will get on this step:


5. Adding joined tables’ independent conditions to the WHERE clause

We have one independent condition left. Let’s add it to the WHERE clause:

SELECT
 TabularSection.Warehouse AS Warehouse,
 TabularSection.MaterialOrService AS Material,
 BalanceTable.QuantityBalance AS QuantityBalance
FROM
 Document.Services.MaterialsAndServices AS TabularSection
  INNER JOIN AccumulationRegister.BalanceOfMaterials.Balance AS BalanceTable
  ON (BalanceTable.Material = TabularSection.MaterialOrService)
   AND (BalanceTable.Warehouse = TabularSection.Warehouse)
WHERE
 TabularSection.Ref = &Ref
 AND TabularSection.MaterialOrService = &Material
 AND BalanceTable.QuantityBalance < 0

And this is the final result we get:




 Lesson 3-3 | Course description | Lesson 3-5 

1C:Enterprise Developer's Community