SITMUC 2024

Clean SQLScript

write code that your colleagues don't hate you for

Jörg Brandeis

(C) Brandeis Consulting

Jörg Brandeis

Roles

  • Trainer for SAP Development topics
  • Managing Director of Brandeis Consulting GmbH
  • BW/4HANA Developer & Consultant
  • Author of the Book SQLScript für/for SAP HANA

CV

  • Carpentry apprenticeship [1996-1999]
  • Dipl. Wirtschaftsinformatiker, Uni Mannheim [1999-2004]
  • Intern, developer, Head of ABAP Development at zetVisions AG in Heidelberg [2002-2015]
  • Freelancer [2015]
  • Founding of Brandeis Consulting GmbH [2020]

Clean ABAP talk at a SAP Inside Track 2017 Walldorf

(C) Brandeis Consulting

Brandeis Consulting GmbH

Training Topics

  • SQLScript for BW-Consultants
  • BW/4HANA Delta Training
  • CDS ABAP - Focus on Analytics or S/4
  • ABAP RESTFul Application Programming Model (RAP)
  • HANA Calculation Views
  • Firoi Elements
  • SAPUI5 Basics
  • Eclipse ADT
  • ABAP Cloud & Modern ABAP
  • Unit Testing in ABAP

The next events

  • Training CDS ABAP in Mannheim, 4. & 5. November
  • Training SQLScript für BW-Berater in Mannheim, 2. - 4. December
  • Conference ABAPConf, 5. December, online/Mannheim/Vienna
(C) Brandeis Consulting

S/4FIT Workshops together with Cadaxo

(C) Brandeis Consulting

Disclaimer

Some lines of code in this presentation were made on dry land and have never swum in the water of a project. The life jacket of a valid syntax check is therefore missing. The point is to show patterns and principles. Please forgive me if errors have crept in.

(C) Brandeis Consulting

Quote

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Fowler

(C) Brandeis Consulting

Some Examples

And 1500 lines more of this!

(C) Brandeis Consulting

OK, let's change the font size or screen resolution

(C) Brandeis Consulting

Another real-world example


SELECT CASE WHEN ls_tmax_ut > '000000000000000' and ls_tmax_ut < '99991231235959' THEN
CAST ( TO_VARCHAR( UTCTOLOCAL( TO_TIMESTAMP (
SUBSTRING( :ls_tmax_ut,1, 12  )
|| REPLACE(SUBSTRING( :ls_tmax_ut, 13, 2  ), '60', '59' )
), 'CET' ), 'YYYYMMDDHH24MISS' ) as DEC ( 15,0 ) )
ELSE CAST ( :ls_tmax_ut as DEC ( 15,0 ) ) END
INTO ls_tsmax_lt FROM DUMMY;

WTF!

(C) Brandeis Consulting

Code that we hate!

(C) Brandeis Consulting

Whats about classic Clean Code?

Jörg: List me the top 10 clean code principles

Chat GPT:
Sure! Here's a more concise version of the top 10 clean code principles:

  1. Meaningful Names
  2. Single Responsibility Principle (SRP)
  3. Keep Functions Small
  4. Avoid Duplication (DRY)
  5. Consistent Formatting
  6. Minimize Nested Control Flow
  7. Single Level of Abstraction in Functions
  8. Minimal Comments, Prefer Readable Code
  9. Graceful Error Handling
  10. Manage Dependencies
(C) Brandeis Consulting

How will our SQL code be executed?

Show me all Tasks

  • with the first- and lastname of the assignee and
  • with the project
  • and with the first- and lastname of the project manager

(C) Brandeis Consulting

Two different Approaches

DO BEGIN
  SELECT  task.id,
          task.assignee,
          task.project,
          assignee.firstname AS as_firstname,
          assignee.lastname  AS as_surname,
          project.id         AS p_id,
          pm.id              AS pl_id,
          pm.firstname       AS pm_firstname,
          pm.lastname        AS pm_lastname

  FROM tasks            AS task

  LEFT OUTER JOIN users AS assignee
  ON task.assignee = assignee.id

  LEFT OUTER JOIN projects AS project
  ON task.project     = project.id

  LEFT OUTER JOIN users AS pm
  ON project.project_manager = pm.id     ;
END;
DO BEGIN
    lt_tasks =
        SELECT id, assignee, project
        FROM tasks;

    lt_tasks_with_assignee =
        SELECT tasks.*,
               assignee.firstname,
               assignee.lastname
        FROM :lt_tasks AS tasks
        LEFT OUTER JOIN users AS assignee
            ON tasks.assignee = assignee.id;

    lt_with_project =
        SELECT tmp.*,
               project.id AS projektid,
               project.project_manager
        FROM :lt_tasks_with_assignee AS tmp
        LEFT OUTER JOIN projects AS project
            ON tmp.project = project.id;

    lt_result =
        SELECT tmp.*,
               pm.firstname AS PL_firstname,
               pm.lastname AS PL_surname
        FROM :lt_with_project AS tmp
        LEFT OUTER JOIN users AS pm
            ON pm.id = tmp.project_manager;

    SELECT *
    FROM :lt_result;

END;

Which one is better to debug? To Read?
Which one will use less memory? Is faster?

(C) Brandeis Consulting

Both lead to the very same execution plan!

(C) Brandeis Consulting

Two similar Queries

DO BEGIN
  SELECT  task.id,
          task.assignee,
          task.project,
          assignee.firstname AS as_firstname,
          assignee.lastname  AS as_surname,
          project.id         AS p_id,
          pm.id              AS pl_id,
          pm.firstname       AS pm_firstname,
          pm.lastname        AS pm_lastname

  FROM tasks            AS task

  LEFT OUTER JOIN users AS assignee
  ON task.assignee = assignee.id

  LEFT OUTER JOIN projects AS project
  ON task.project     = project.id

  LEFT OUTER JOIN users AS pm
  ON project.project_manager = pm.id

  where task.status in (1, 2)   ;
END;
DO BEGIN
  SELECT  task.id,
          task.assignee,
          task.project,
          assignee.firstname AS as_firstname,
          assignee.lastname  AS as_surname,
          project.id         AS p_id,
          pm.id              AS pl_id,
          pm.firstname       AS pm_firstname,
          pm.lastname        AS pm_lastname

  FROM tasks            AS task

  LEFT OUTER JOIN users AS assignee
  ON task.assignee = assignee.id

  LEFT OUTER JOIN projects AS project
  ON task.project     = project.id

  LEFT OUTER JOIN users AS pm
  ON project.project_manager = pm.id

  where task.status = 1   ;
END;
(C) Brandeis Consulting

The two approaches lead to an execution plan on different engines

(C) Brandeis Consulting

An other example

Once an Oracle Expert told me, that the NOT EXISTS predicate is slow and should be avoided. A LEFT OUTER JOINwith filtering on the NULL-Values is much faster.

We tried both approaches, but the result is the 100% exact same logical execution plan on SAP HANA!

select *
  from users as u
  where not exists (select *
                      from tasks as t
                     where u.id = t.assignee );
select u.*
  from users as u
  left outer join tasks as t
  on u.id = t.assignee
  where t.id is null;

The first statment is much more readable, because it is obvious that

  • I am interested in data of the users table
  • The data should be filtered
(C) Brandeis Consulting

Conclusion

  • It's only important, what result set you are describing with your code
  • For the HANA Database it is less important, how you describe your Result
  • You can't control the optimizer by writing code in a certain way
(C) Brandeis Consulting

Readability and performance are not contradictions

(C) Brandeis Consulting

Formatting (1/2) - Consistent Formatting

The first and most obvious topic for readability is formatting.

Line Breaks

Between

  • the fields
  • the different clauses
  • multiple conditions, combined with AND and OR
  • Tablenames in the USING-Clause of AMDP

Aligning

  • fieldlists
  • AS
  • USING-Clause
(C) Brandeis Consulting

Formatting (2/2) - Consistent Formatting

When you don't want to split your expressions into smaller bits (later more about this), you should at least format them well:

    COALESCE(
        ROUND(
            TO_DECIMAL(B.ContractorSideServiceCurrency) * A.CumulatedOrderQuantity / A.TotalOrderQuantity, 2
        ) - ROUND(
            TO_DECIMAL(B.ContractorSideServiceCurrency) * (A.CumulatedOrderQuantity - A.OrderQuantity)
            / A.TotalOrderQuantity, 2
        ), 0
    ) AS ContractorSideServiceCurrency
(C) Brandeis Consulting

Don't omit optional keywords - Consistent Formatting

  • AS is optional
  • LEFT JOIN is the same as LEFT OUTER JOIN
  • RIGHT JOIN is the same as RIGHT OUTER JOIN
  • FULL JOIN is the same as FULL OUTER JOIN
  • JOIN is the same as INNER JOIN

The optional keywords improve the readability!

Without optional keywords

select a b,
       c.d e
  from dbtab1 c
  left join dbtab2 f
  on c.id = f.id;

The same query

select a   as b,
       c.d as e
  from dbtab1 as c
  left outer join dbtab2 as f
  on c.id = f.id;
(C) Brandeis Consulting

Avoid complexity within one statement - Single Level of Abstraction

Each statement should be trivial.

Don't mix multiple operations within one statement.

  • Calculation of fields with expressions like CASE or SQL-Functions
  • Mapping/Renaming of all fields
  • Combining tables with JOIN and the set operations like UNION
  • Aggregation with GROUP BY
  • Non-trivial WHERE-Conditions, e.g. with sub-queries
(C) Brandeis Consulting

Use meaningful names for table variables (TV) - Meaningful Names

  • Explain, what is inside
  • Focus on the difference
(C) Brandeis Consulting

Use correlation names aka. table alias names - Meaningful Names

When more than a single table is queried, all tables should get an short and helpful correlation names. When you use abbreviations, explain them when they are not obvious.

  select soi.SalesOrder,
         soi.SalesOrderItem,
         soi.Product,
         so.soldToParty  
    from I_SalesOrderItem as soi 
    inner join I_SalesOrder as so 

Fields should allways be addressed by their full name: CorrelationName.fieldname
Even when they are unique!

Don't use A, B and C as correlation names!!!

(C) Brandeis Consulting

Map fieldnames to meaningful fieldnames (1/2) - Meaningful Names

Similar to the Virtual Data Model (VDM). When you can refer to Views of the VDM, this is of course preferable.

Only required for larger routins

    Items =
        SELECT
            EBELN,
            EBELP,
            BSART,
            LIFNR,
            EKGRP,
            WAERS,
            BEDAT,
            PROCSTAT,
            EMATN,
            WERKS,
            MEINS,
            INCO2L,
            INCO1,
            BANFN,
            BNFPO,
            LPRIO,
            KNUMV
Items =
    SELECT
        EBELN    AS PurchaseOrderNumber,
        EBELP    AS PurchaseOrderItem,
        BSART    AS DocumentType,
        LIFNR    AS VendorNumber,
        EKGRP    AS PurchasingGroup,
        WAERS    AS Currency,
        BEDAT    AS DocumentDate,
        PROCSTAT AS ProcessingStatus,
        EMATN    AS MaterialNumber,
        WERKS    AS Plant,
        MEINS    AS UnitOfMeasure,
        INCO2L   AS IncotermsLocation,
        INCO1    AS Incoterms,
        BANFN    AS PurchaseRequisitionNumber,
        BNFPO    AS PurchaseRequisitionItem,
        LPRIO    AS DeliveryPriority,
        KNUMV    AS ConditionRecordNumber
(C) Brandeis Consulting

Map to meaningful fieldnames (2/2) - Meaningful Names

Try to find unique fieldnames

When you have multiple DB-Tables with the same fieldname, you should try to make them unique. This reduces the problems with ambigiously defined columns. It makes it easier to use the asterisk * in the fieldlist

Let ChatGPT do the hard work!

The mapping on the previous slide was generated by ChatGPT!

kannst du bitte für diese Feldliste im SQL schöne englische Alias-Namen im Pascal-Case mit AS hinzufügen:

(C) Brandeis Consulting

Inbound Projections - Meaningful Names, Manage Dependencies

Create a table variable for each database table:

  • give the TV a reasonable name
  • reduce the fieldlist to the required fields
  • rename the fields to meaningful, unique names

Don't refer to database tables later!

(C) Brandeis Consulting

Avoid mentioning all fields all the time (1/2) Keep Functions Small,
Avoid Duplication (DRY)


The Problem: Statements with hundreds and more lines


    SELECT
        PurchaseOrderNumber,
        PurchaseOrderItem,
        DocumentType,
        VendorNumber,
        PurchasingGroup,
        Currency,
        DocumentDate,
        ProcessingStatus,
        MaterialNumber,

   ...

        UnitOfMeasure,
        IncotermsLocation,
        Incoterms,
        PurchaseRequisitionNumber,
        PurchaseRequisitionItem,
        DeliveryPriority,
        ConditionRecordNumber,
        ABS(ExchangeRate) AS ExchangeRate,
        CASE WHEN ExchangeRate < 0 THEN '1' ELSE '0' END AS IsInverseExchangeRate,
        ConversionNumerator,
        ConversionDenominator,
        ROUND(NetOrderValue * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS NetOrderValue,
        NetOrderValue AS NetOrderValueCurrency,
        ROUND(ActivationValue * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS ActivationValue,
        ActivationValue AS ActivationValueCurrency,
        ROUND(GrossPurchasePrice * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS GrossPurchasePrice,
        GrossPurchasePrice AS GrossPurchasePriceCurrency,
        ROUND(NetPurchasePrice * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS NetPurchasePrice,
        NetPurchasePrice AS NetPurchasePriceCurrency
    FROM :inTab


(C) Brandeis Consulting

Avoid mentioning all fields all the time (2/2) - Keep Functions Small,
Avoid Duplication (DRY)


**Use the asterisk \* when possible** Reduce your statement to the fields, that were calculated.
    SELECT
        *,
        ABS(ExchangeRate) AS AbsExchangeRate,
        CASE WHEN ExchangeRate < 0 THEN '1' ELSE '0' END AS IsInverseExchangeRate,
        ROUND(ActivationValue * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS ActivationValue,
        ROUND(GrossPurchasePrice * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS GrossPurchasePrice,
        ROUND(NetPurchasePrice * CASE WHEN ExchangeRate >= 0 THEN ExchangeRate ELSE -1 / ExchangeRate END, 2) AS NetPurchasePrice,
    FROM :inTab

The * with multiple tables

select it.*,
       src.NetAmount,
       src.TaxAmount
  from :inTab as it
  left outer join :sourceDSO as src
  on ...

In case of naming conflicts

When the fieldname for the calculated fields is already in use in the fieldlist covered by the asterisk *, than you have to rename the field. A prefix is a good option, e.g. Calc_

(C) Brandeis Consulting

Last step of a BW routine is an outbound projection

The very last step, that should be without any further logic, is the mapping to the structure of the result table outTab.

  • Bring the fields in the correct order
  • Rename the fields

Please ask ChatGPT to help you with this step

outTab =
    SELECT
        PurchaseOrderNumber        AS EBELN,
        PurchaseOrderItem          AS EBELP,
        DocumentType               AS BSART,
        VendorNumber               AS LIFNR,
        PurchasingGroup            AS EKGRP,
        Currency                   AS WAERS,
        DocumentDate               AS BEDAT,
        ProcessingStatus           AS PROCSTAT,
        MaterialNumber             AS EMATN,
        Plant                      AS WERKS,
        UnitOfMeasure              AS MEINS,
        IncotermsLocation          AS INCO2L,
        Incoterms                  AS INCO1,
        PurchaseRequisitionNumber  AS BANFN,
        PurchaseRequisitionItem    AS BNFPO,
        Calc_DeliveryPriority      AS LPRIO,
        ConditionRecordNumber      AS KNUMV
  from :previousStep;
(C) Brandeis Consulting

Don't use nested subqueries in the FROM-Clause - Minimize Nested Control Flow

Use tablevariables instead of the subqueries. Benefits:

  • The reading flow is from top to bottom
  • Each tablevariable can get a descriptive, meaningful name
  • Each query is easy to read

The optimizer uses the exact same execution plan!

(C) Brandeis Consulting

Avoid multiple identical expressions in the fieldlist - Avoid Duplication (DRY)

nextStep = SELECT *,
    (((ManagementFeeCurrency) * CumulatedOrderQuantity / TotalOrderQuantity) - ((ManagementFeeCurrency) * (CumulatedOrderQuantity - OrderQuantity) / TotalOrderQuantity)) AS ManagementFeeCurrency,
    (((ComplaintAllowance) * CumulatedOrderQuantity / TotalOrderQuantity) - ((ComplaintAllowance) * (CumulatedOrderQuantity - OrderQuantity) / TotalOrderQuantity)) AS ComplaintAllowance,
    (((ComplaintAllowanceCurrency) * CumulatedOrderQuantity / TotalOrderQuantity) - ((ComplaintAllowanceCurrency) * (CumulatedOrderQuantity - OrderQuantity) / TotalOrderQuantity)) AS ComplaintAllowanceCurrency,
    (((DistributionCosts) * CumulatedOrderQuantity / TotalOrderQuantity) - ((DistributionCosts) * (CumulatedOrderQuantity - OrderQuantity) / TotalOrderQuantity)) AS DistributionCosts,
    (((DistributionCostsCurrency) * CumulatedOrderQuantity / TotalOrderQuantity) - ((DistributionCostsCurrency) * (CumulatedOrderQuantity - OrderQuantity) / TotalOrderQuantity)) AS DistributionCostsCurrency,
...
FROM :previousStep

The query contains repeating parts, that can be calculated in a previous step. DRY!

previousStep = select *,
                      ( CumulatedOrderQuantity - OrderQuantity ) / TotalOrderQuantity as RemainingQuantityFactor,
                      CumulatedOrderQuantity / To talOrderQuantity                 as QuantityFactor,
                 from :inTab;

nextStep = SELECT *,
                  ManagementFeeCurrency * QuantityFactor - ManagementFeeCurrency * RemainingQuantityFactor           AS ManagementFeeCurrency,
                  ComplaintAllowance * QuantityFactor - ComplaintAllowance * RemainingQuantityFactor                 AS ComplaintAllowance,
                  ComplaintAllowanceCurrency * QuantityFactor - ComplaintAllowanceCurrency * RemainingQuantityFactor AS ComplaintAllowanceCurrency,
                  DistributionCosts * QuantityFactor - DistributionCosts * RemainingQuantityFactor                   AS DistributionCosts,
                  DistributionCostsCurrency * QuantityFactor - DistributionCostsCurrency * RemainingQuantityFactor   AS DistributionCostsCurrency,
 ...
FROM :previousStep
(C) Brandeis Consulting

Ask ChatGPT to find common expressions - Avoid Duplication (DRY)

"Can you please find common expressions in fieldlist of the following sql-statement. List them and tell me, how often they were used. "

(C) Brandeis Consulting

For complex expressions: seperate technical from business logic
Single Responsibility Principle


Step1 = select *,
               COALESCE(ROUND(TO_DECIMAL(B.FinancingCosts) * A.CumulatedOrderQuantity / A.TotalOrderQuantity, 2)
               - ROUND(TO_DECIMAL(B.FinancingCosts) * (A.CumulatedOrderQuantity - A.OrderQuantity) / A.TotalOrderQuantity, 2), 0) AS FinancingCosts,
               COALESCE(ROUND(TO_DECIMAL(B.FinancingCostsCurrency) * A.CumulatedOrderQuantity / A.TotalOrderQuantity, 2)
               -  ROUND(TO_DECIMAL(B.FinancingCostsCurrency) * (A.CumulatedOrderQuantity - A.OrderQuantity) / A.TotalOrderQuantity, 2), 0) AS FinancingCostsCurrency
                  from :inTab;

Functions like

  • COALESCE or IFNULL
  • TO_DECIMAL
  • ROUND

were necessary for technical reasons. They add noise to the expression that obscures the clear view of the business requirement.

These sql functions can often be done in an additional previous or subsequent step without business logic.

(C) Brandeis Consulting

Multiple CASE Expressions with the same condition - Avoid Duplication (DRY)

Split into multiple, disjunct table variables. Each TV is trivial. Combine them afterwords with UNION ALL.

tabVar =
  SELECT *,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE SeaFreightCurrency
           END                                AS SeaFreightCurrency,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE HeavyWeight
           END                                AS HeavyWeight,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE HeavyWeightCurrency
           END                                AS HeavyWeightCurrency,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE Customs
           END                                AS Customs,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE CustomsCurrency
           END                                AS CustomsCurrency,
     ...
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE HandlingCharge
           END                                AS HandlingCharge,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE HandlingChargeCurrency
           END                                AS HandlingChargeCurrency,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE ManagementFee
           END                                AS ManagementFee,
         CASE WHEN XYIndicat = 'S'  THEN 0
              ELSE ManagementFeeCurrency
           END                                AS ManagementFeeCurrency
  FROM :inTab;



   XYIndicatS = SELECT  0 AS SeaFreightCurrency    ,
                        0 AS HeavyWeight           ,
                        0 AS HeavyWeightCurrency   ,
                        0 AS Customs               ,
                        0 AS CustomsCurrency       ,
                 ...
                        0 AS HandlingCharge        ,
                        0 AS HandlingChargeCurrency,
                        0 AS ManagementFee         ,
                        0 AS ManagementFeeCurrency
                 from :inTab
                 where XYIndicat = 'S';

   XYIndicatNotS = SELECT SeaFreightCurrency    ,
                          HeavyWeight           ,
                          HeavyWeightCurrency   ,
                          Customs               ,
                          CustomsCurrency       ,
                  ...
                          HandlingCharge        ,
                          HandlingChargeCurrency,
                          ManagementFee         ,
                          ManagementFeeCurrency
                  from :inTab
                  where XYIndicat <> 'S';

    tabVar = select * from :XYIndicatS
             union ALL
             select * from :XYIndicatNotS;
(C) Brandeis Consulting

Filter in the WHERE-Clause - Principle of Least Astonishment

Use JOIN, when you want data from the other table, use a WHERE-Clause when you want to filter!

An INNER JOIN can be used to filter. But when you don't want data from the other side, then you should use an IN-predicate or an EXISTS-predicate in the WHERE-Clause instead.

An other example of a JOIN for filtering:

select *
  from users as u
  where not exists (select *
                      from tasks as t
                     where u.id = t.assignee );
select u.*
  from users as u
  left outer join tasks as t
  on u.id = t.assignee
  where t.id is null;
(C) Brandeis Consulting

No logic in a UNION Keep Functions Small

SQL queries are often very long. When we combine them with set operations, they become even longer. Therefore, the individual queries in a set operation or UNION ALL should be trivial.

relevantRecords = select field1, 
                        expression as field2

                     ...100 lines more

                     from :offers

                  UNION ALL

                  select field1, 
                        expression as field2

                     ...100 lines more

                     from :invoices;
relevantOffers = select field1, 
                        expression as field2

                     ...100 lines more

                     from :offers; 

relevantInvoices =  select field1, 
                           expression as field2

                        ...100 lines more

                        from :invoices;

relevantRecords = select * from relevantOffers
                  union all
                  select * from relevantInvoices;


(C) Brandeis Consulting

Don't omit brackets in logical expressions

One of the most common SQL errors is the combination of predictes with AND and OR.

Example of missleading code

SELECT * 
  FROM Tasks
 WHERE Assignee = '4711' 
   AND Status = 1
    or Status = 2;

What is the effect?

SELECT * 
  FROM Tasks
 WHERE Assignee = '4711' 
   AND (Status = 1
     or Status = 2);

or

SELECT * 
  FROM Tasks
 WHERE (Assignee = '4711' 
   AND  Status = 1)
     or Status = 2;
(C) Brandeis Consulting

Comments - Minimal Comments, Prefer Readable Code

  • Each SELECT-Query could get a short comment, what it should do.
  • Complex or not self explaining things should be expained, e.g. regular expressions
(C) Brandeis Consulting

Check of the Top 10 Clean Code Principles

  1. Meaningful Names 🗸 - Table variable names, Correlation Names, Map fieldnames
  2. Single Responsibility Principle (SRP) - Separate technical logic from business logic
  3. Keep Functions Small 🗸 - Keep SELECT queries small
  4. Avoid Duplication (DRY) 🗸 - Avoid duplicate expressions in the fieldlist, avoid identical conditions in CASE
  5. Consistent Formatting 🗸 - Yes!!!
  6. Minimize Nested Control Flow 🗸 - Avoid nested subqueries in the FROM-Clause
  7. Single Level of Abstraction in Functions 🗸 - Avoid mixing different opperations in one statement
  8. Minimal Comments, Prefer Readable Code 🗸 - Yes
  9. Graceful Error Handling
  10. Manage Dependencies 🗸 - All references to DB-Tables where wrapped by an Inbound Projection
(C) Brandeis Consulting

Thank You for your attention

(C) Brandeis Consulting

```sql select * from (select sum(.... from ( select ... from ( select ... from dbTabXY where x = 42 ) left outer join (select ... from dbTabAB where z = 'C' ) on ) where y = '0815' group by abc ) ``` </div><div> ```sql tabVar1 = select ... from dbTabAB: tabVar2 = select ... from dbTabXY where x = 42; tabVar3 = select ... from :tabVar2 left outer join :tabVar1 on ... ; tabVar4 = select sum(.... from :tabVar3 where x = 42 group by abc select ... from :tabVar4; ```