Function in PostgreSQL to insert from one table to

2019-03-06 08:00发布

问题:

I have the following:

I got the tables:

Equipos (Teams)

Partidos (Matches)

The columns num_eqpo_loc & num_eqpo_vis from the table partidos reference to the table equipo. They reference to the num_eqpo column. As you can see here:

create table equipos
(num_eqpo serial,     
ciudad varchar (30),
num_gpo int, 
nom_equipo varchar (30), 
primary key (num_eqpo), 
foreign key (num_gpo) references grupos (num_gpo))

create table partidos 
(semana int, 
num_eqpo_loc int, 
num_eqpo_vis int, 
goles_loc int, 
goles_vis int, primary key (semana,num_eqpo_loc,num_eqpo_vis), 
foreign key (num_eqpo_loc) references equipos (num_eqpo), 
foreign key (num_eqpo_vis) references equipos (num_eqpo))

I want to get the following output:

In one hand, I created a table called general:

CREATE TABLE general
(
  equipo character varying(30) NOT NULL,
  partidos_jug integer,
  partidos_gana integer,
  partidos_emp integer,
  partidos_perd integer,
  puntos integer,
  goles_favor integer,
  CONSTRAINT general_pkey PRIMARY KEY (equipo)
)

In the other, I have the function:

CREATE OR REPLACE FUNCTION sp_tablageneral ()  RETURNS TABLE (
    equipo character varying(30)
  , partidos_jug int
  , partidos_gana int
  , partidos_emp int
  , partidos_perd int
  , puntos int
  , goles_favor int) AS
$BODY$
DECLARE cont int:= (SELECT count(num_eqpo)FROM equipos);
r partidos%ROWTYPE;
BEGIN

    while cont>0
    LOOP

    SELECT INTO equipo nom_equipo FROM equipos AS E WHERE E.num_eqpo=cont;
    SELECT INTO partidos_jug COUNT(*) FROM partidos as P WHERE (P.num_eqpo_loc=cont OR P.num_eqpo_vis=cont);
    SELECT INTO partidos_gana COUNT(*) FROM partidos AS P WHERE (P.num_eqpo_loc=cont AND P.goles_loc>P.goles_vis OR P.num_eqpo_vis=cont AND P.goles_vis>P.goles_loc);
    SELECT INTO partidos_emp COUNT(*) FROM partidos AS P WHERE (P.num_eqpo_loc=cont AND P.goles_loc=P.goles_vis OR P.num_eqpo_vis=cont AND P.goles_loc=P.goles_vis);
    SELECT INTO partidos_perd COUNT(*) FROM partidos as P WHERE ( (P.num_eqpo_loc=cont AND P.goles_loc<P.goles_vis) OR (P.num_eqpo_vis=cont AND P.goles_loc>P.goles_vis));
    SELECT INTO puntos partidos_emp*1 + partidos_gana*3;
    SELECT INTO goles_favor SUM(goles_loc) FROM partidos as P WHERE P.num_eqpo_loc=cont + (SELECT SUM(goles_vis) FROM partidos as P WHERE P.num_eqpo_vis=cont);

    cont:= cont - 1;
    END LOOP;

  RETURN NEXT ; 
 END;
$BODY$ LANGUAGE plpgsql STABLE;

I want the function to show my desired Output & I also want the table 'General' to have the same values from the desired output.

With this function I just get:

I don't know how to see the desired content as I just get the first row of data. I also wonder how to Insert from the table returned by the fuction to the existing table called General.

Edit: I have also tried with:

CREATE OR REPLACE FUNCTION sp_tablageneral ()  RETURNS TABLE (
    equipo character varying(30)
  , partidos_jug int
  , partidos_gana int
  , partidos_emp int
  , partidos_perd int
  , puntos int
  , goles_favor int) AS
$BODY$
DECLARE cont int:= (SELECT count(num_eqpo)FROM equipos);
r partidos%ROWTYPE;
BEGIN

    while cont>0
    LOOP

        SELECT INTO equipo nom_equipo FROM equipos AS E WHERE E.num_eqpo=cont;
    SELECT INTO partidos_jug COUNT(*) FROM partidos as P WHERE (P.num_eqpo_loc=cont OR P.num_eqpo_vis=cont);
    SELECT INTO partidos_gana COUNT(*) FROM partidos AS P WHERE (P.num_eqpo_loc=cont AND P.goles_loc>P.goles_vis OR P.num_eqpo_vis=cont AND P.goles_vis>P.goles_loc);
    SELECT INTO partidos_emp COUNT(*) FROM partidos AS P WHERE (P.num_eqpo_loc=cont AND P.goles_loc=P.goles_vis OR P.num_eqpo_vis=cont AND P.goles_loc=P.goles_vis);
    SELECT INTO partidos_perd COUNT(*) FROM partidos as P WHERE ( (P.num_eqpo_loc=cont AND P.goles_loc<P.goles_vis) OR (P.num_eqpo_vis=cont AND P.goles_loc>P.goles_vis));
    SELECT INTO puntos partidos_emp*1 + partidos_gana*3;
    SELECT INTO goles_favor SUM(goles_loc) FROM partidos as P WHERE P.num_eqpo_loc=cont + (SELECT SUM(goles_vis) FROM partidos as P WHERE P.num_eqpo_vis=cont);

    SELECT equipo, partidos_jug , partidos_gana, partidos_emp , partidos_perd , puntos , goles_favor INTO equipo,partidos_jug,partidos_gana,partidos_emp,partidos_perd,puntos,goles_favor FROM general;

    cont:= cont - 1;
        END LOOP;

  RETURN NEXT ; 
 END;
$BODY$ LANGUAGE plpgsql STABLE;

But I get:

ERROR: the reference to the column "equipo" is ambiguous 
LINE 1: SELECT equipo , partidos_jug, partidos_gana, partidos_emp ...
                ^
********** Error **********
ERROR: the reference to the column "equipo" is ambiguous 
SQL state: 42702
Detail: It could refer either to a variable PL / pgSQL as a column in a table.
Context: PL / pgSQL sp_tablageneral () function on line 17 in SQL statement

Any help would be amazing.

Thanks in advance!

回答1:

You can solve this issue in pure SQL, you don't need a function for this.

The best thing is to break the collection of statistics into two distinct queries, one for when the team plays at home, one when they play away. For each game calculate the points and the goals scored. Then UNION those two queries and use that as a sub-query to calculate the overall stats:

SELECT
  eq.nom_equipo AS equipo,
  COUNT(p.*) AS partidos_jug,
  SUM(CASE WHEN p.puntos = 3 THEN 1 ELSE 0 END) partidos_gana,
  SUM(CASE WHEN p.puntos = 1 THEN 1 ELSE 0 END) partidos_emp,
  SUM(CASE WHEN p.puntos = 0 THEN 1 ELSE 0 END) partidos_perd,
  SUM(p.puntos) AS puntos,
  SUM(p.goles) AS goles_favor
FROM equipos eq
JOIN (
  -- Playing at home
  SELECT
    num_eqpo_loc AS eqpo, 
    CASE WHEN (goles_loc > goles_vis) THEN 3
         WHEN (goles_loc = goles_vis) THEN 1
         ELSE 0
    END AS puntos,
    goles_loc AS goles
  FROM partidos
  UNION
  -- Playing away
  SELECT
    num_eqpo_vis AS eqpo, 
    CASE WHEN (goles_vis > goles_loc) THEN 3
         WHEN (goles_vis = goles_loc) THEN 1
         ELSE 0
    END AS puntos,
    goles_vis AS goles
  FROM partidos) AS p ON p.eqpo = eq.num_eqpo
GROUP BY equipo
ORDER BY puntos DESC, partidos_jug ASC, goles_favor DESC;

This is not particularly fast due to the CASE statements, but it will be faster than using a procedure and a loop.

Instead of putting the result of this query into a table, I would suggest that you CREATE VIEW general AS ... with the above query. In that case you always get the latest results when you SELECT * FROM general and you don't have to TRUNCATE the general table before running the query (adding new results with data in the table will violate the PK constraint). If you really need the table then use SELECT ... INTO general FROM ... in the query above.