PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Ляво външно съединение, действащо като вътрешно съединение

Заявката вероятно може да бъде опростена до:

SELECT u.name AS user_name
     , p.name AS project_name
     , tl.created_on::date AS changeday
     , coalesce(sum(nullif(new_value, '')::numeric), 0)
     - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
FROM   users             u
LEFT   JOIN (
        tasks            t 
   JOIN fixins           f  ON  f.id = t.fixin_id
   JOIN projects         p  ON  p.id = f.project_id
   JOIN task_log_entries tl ON  tl.task_id = t.id
                           AND  tl.field_id = 18
                           AND (tl.created_on IS NULL OR
                                tl.created_on >= '2013-09-08' AND
                                tl.created_on <  '2013-09-09') -- upper border!
       ) ON t.assignee_id = u.id
WHERE  EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
GROUP  BY 1, 2, 3
ORDER  BY 1, 2, 3;

Това връща всички потребители, които някога са имали някаква задача.
Плюс данни за проекти и дни където съществуват данни в посочения период от време в task_log_entries .

Основни точки

  • агрегирана функция sum() игнорира NULL стойности. COALESCE() на ред вече не се изисква веднага щом преработите изчислението като разлика от две суми:

     ,coalesce(sum(nullif(new_value, '')::numeric), 0) -
      coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
    

    Въпреки това, ако възможно е всички колони от селекция имат NULL или празни низове, увийте сумите в COALESCE веднъж.
    Използвам numeric вместо float , по-безопасна алтернатива за минимизиране на грешките при закръгляване.

  • Вашият опит да получите отделни стойности от обединяването на users и tasks е безсмислено, тъй като се присъединявате към task още веднъж по-надолу. Изравнете цялата заявка, за да я направите по-проста и по-бърза.

  • Тези позиционни препратки са само за удобство при обозначения:

    GROUP BY 1, 2, 3
    ORDER BY 1, 2, 3
    

    ... правите същото като в първоначалната си заявка.

  • За да получите date от timestamp можете просто да прехвърлите към date :

    tl.created_on::date AS changeday
    

    Но е много по-добре да тествате с оригинални стойности в WHERE клауза или JOIN условие (ако е възможно и е възможно тук), така че Postgres може да използва обикновени индекси в колоната (ако има):

     AND (tl.created_on IS NULL OR
          tl.created_on >= '2013-09-08' AND
          tl.created_on <  '2013-09-09')  -- next day as excluded upper border
    

    Обърнете внимание, че литерал за дата се преобразува в timestamp в 00:00 от деня в текущия ви час зона . Трябва да изберете следващия ден и изключете като горна граница. Или предоставете по-ясен литерал на клеймо като '2013-09-22 0:0 +2':: timestamptz . Повече за изключването на горната граница:

  • За изискването every user who has ever been assigned to a task добавете WHERE клауза:

    WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
    
  • Най-важното :LEFT [OUTER] JOIN запазва всички редове отляво на съединението. Добавяне на WHERE клауза от вдясно таблицата може да анулира този ефект. Вместо това преместете филтърния израз в JOIN клауза Повече обяснения тук:

  • Скоби може да се използва за налагане на реда, в който се свързват таблиците. Рядко необходим за прости заявки, но много полезен в този случай. Използвам функцията, за да се присъединя към task , fixins , projects и task_log_entries преди да го присъедините към users - без подзаявка.

  • Псевдоними на таблици улесняват писането на сложни заявки.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. postgres - ГРЕШКА:операторът не съществува

  2. PostgreSql INSERT FROM SELECT RETURNING ID

  3. Коригирайте „ГРЕШКА:  всяка заявка ИЗКЛЮЧЕН трябва да има същия брой колони“ в PostgreSQL

  4. ФАТАЛНО:не можах да осъществя достъп до файла с частен ключ /etc/ssl/private/ssl-cert-snakeoil.key:разрешението е отказано

  5. Преобразувайте дата в Юлиански ден в PostgreSQL