PostgreSQL - left join i group by

PostgreSQL - left join i group by
MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 5 lat
  • Postów:243
0

5a220f535e.png
2f87efa205.png

Tu mam zadanie z podaną już odpowiedzią.

73e10ce8b5.png

Oto mój kod:

Kopiuj
select nazwa,count(*) as liczba,round(avg(placa_pod),1) as placa
from pracownicy p
right join zespoly z on z.id_zesp=p.id_zesp
group by nazwa
order by nazwa;

Niestety wypluwa on coś takiego

7deead9982.png

Co zrobić, żeby było dobrze bo już nie mam pomysłów?

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:8 miesięcy
  • Postów:6610
0

zamień right na left


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 5 lat
  • Postów:243
0

To teraz mam coś takiego. Dalej to nie jest to co w odpowiedzi.

fe44a96e2f.png

AB
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 8 lat
  • Postów:229
1

Specjalistą nie jestem od baz danychy, ale bym zrobił tak:

Kopiuj

\timing off
\pset pager off

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;


\c postgres;
DROP DATABASE IF EXISTS test2;
DROP ROLE IF EXISTS test2;

CREATE ROLE test2 PASSWORD 'test2' NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;
CREATE DATABASE test2 OWNER test2 ENCODING 'UTF-8';
\c test2;
set role test2;


CREATE TABLE countries (
    id   serial                NOT NULL,
    name character varying(10) NOT NULL
);


CREATE TABLE users (
    id   serial                NOT NULL,
    id_country integer         NOT NULL,
    name character varying(10) NOT NULL,
    price double precision
);


create or replace function random_string(length integer) returns text as 
$$
declare
  chars text[] := '{A,B,C,D,E,F,G,H}';
  result text := '';
  i integer := 0;
begin
  for i in 1..length loop
    result := result || chars[1+random()*(array_length(chars, 1)-1)];
  end loop;
  return result;
end;
$$ language plpgsql;


create or replace function mk_countries(length integer) returns void as 
$$
declare
  i integer;
begin
  for i in 1..length loop
    execute 'insert into countries (name) values( random_string(5) )' ;
  end loop;
end;
$$ language plpgsql;


create or replace function mk_users(length integer) returns void as 
$$
declare
  i integer;
  p double precision;
  cnt_country integer;
  id_country integer;
begin
  select count(*) into cnt_country from countries;
  for i in 1..length loop
    id_country = floor( random() * cnt_country ); -- zawsze przynajmniej jeden kraj ma zero userów, więc będzie zero w testach
    p = random() * 20000 + 3000;
    execute 'insert into users (id_country,name,price) values( $1 , random_string(10) , $2 )' using id_country, p;
  end loop;
end;
$$ language plpgsql;

select mk_countries(30);
select mk_users(10000);


select 
  c.name as NAZWA, 
  (select count(*) from users as u1 where u1.id_country = c.id) AS LICZBA,
  (select avg(u2.price) from users as u2 where u2.id_country = c.id) AS PLACA
from
  countries as c
order by
  c.name;
  
  
  
MI
A po co te widoki i ogólnie ten cały DDL. Nie da się prościej?
AB
Jakie widoki? Przeież rozwiązanie zajmuje raptem 8 linijek. Reszta to kod generujący przykładowe dane.
MI
Dzięki. Ale wydaje mi się, że można prościej to zrobić wykorzystując joiny bez podzapytań.
AB
Jeśli tak, to chętnie się czegoś nauczę.
1

Na wstępie napiszę, że pracuję w MSSQL i dokładnie nie będę mógł Ci pomóc, jednak sugerując się tylko i wyłącznie twoimi screenami wiem gdzie leży błąd.

Jak można zauważyć z pierwszego postu, wszystko jest dobrze oprócz "Badania operacyjne". Powinno być 0 a jest 1. Dzieje się tak ponieważ połączyłeś nazwę zespołu z pustym pracownikiem (left joint, right joint). Więc twoja średnia dla tego zespołu wynosi 1. W celu naprawienia tego powinieneś liczyć po id pracowniku a nie po *, z tego co przeczytałem to powinno pomóc.

Marcin.Miga
  • Rejestracja:prawie 17 lat
  • Ostatnio:10 dni
  • Postów:2792
1
Kopiuj
SELECT z.nazwa,COUNT(p.id) AS liczba,Coalesce(round(avg(placa_pod),1),0) AS placa
FROM pracownicy p
RIGHT JOIN zespoly z ON z.id_zesp=p.id_zesp
GROUP BY nazwa
ORDER BY nazwa;
MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 5 lat
  • Postów:243
0

Dzięki @Pijany_Kaczor i @Marcin_Miga. Oto chodziło. Powiedzcie mi tylko jaka jest dokładnie różnica między count() a count(id_prac). Count() liczy każdy wiersz w grupie, a count(id_prac) liczy te wiersze w grupie w których id_prac jest różne od null. Dobrze rozumuję?

edytowany 1x, ostatnio: Mikilll
Marcin.Miga
  • Rejestracja:prawie 17 lat
  • Ostatnio:10 dni
  • Postów:2792
0

Count liczy "nie-NULLe". Dla * zawsze masz "nie-NULL". Dla pola z tabeli połączonej LEFT/RIGHT nie zawsze musi być wartość, więc może być NULL.
Dobrze rozumujesz.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.