Vous n'êtes pas identifié(e).
Re,
voila le man de COPY : http://docs.postgresqlfr.org/9.0/sql-copy.html
Pour ton pb, c'est logique qu'il insère l'intégralité du fichier à chaque fois puisqu'il le relis entièrement.
Si ton CSV est alimenté au fur et a mesure, tu peux peut-être tenter un TRUNCATE de la table avant le copy, mais ça implique de recharger la table intégralement à chaque fois.
L'autre solution serait peut être d'effacer le CSV à chaque lecture (ou du moins, le renommer)
Cordialement
Quelle version de postgres ?
Essayes de remplacer la ligne dans le code avec ça : SELECT NEW.firstName||'.'||NEW.lastName || '1'
Il semble ne pas vouloir faire la conversion implicitement.
Remplace également celle ci :
NEW.username := NEW.firstname||'.'||NEW.lastname||cont;
par
NEW.username := NEW.firstname||'.'||NEW.lastname||cont::TEXT;
Cordialement
Bon,
voila a quoi j'arrive (mais ca ne gère pas les suffixes non numériques), par contre, on n'est plus limité à 10 jean.dupont:
CREATE OR REPLACE FUNCTION plip()
RETURNS TRIGGER AS
$$
DECLARE
tmp VARCHAR;
cont integer;
BEGIN
NEW.firstname := initcap(NEW.firstname);
NEW.lastname := initcap(NEW.lastname);
NEW.username := NEW.firstname||'.'||NEW.lastname;
SELECT max(username) into tmp FROM plop where firstname = NEW.firstname and lastname = NEW.lastname;
IF tmp IS NULL THEN
-- ne rien faire
ELSIF tmp = NEW.username THEN
NEW.username := NEW.firstname||'.'||NEW.lastname||1;
ELSE
SELECT MAX(TRIM(username,(NEW.firstname||'.'||NEW.lastname))::INT)+1 INTO cont FROM plop where firstname = NEW.firstname and lastname = NEW.lastname and username != NEW.username;
NEW.username := NEW.firstname||'.'||NEW.lastname||cont;
END IF;
RETURN NEW;
END
$$
LANGUAGE PLPGSQL;
Cordialement
Voila, j'ai finalement réussi
DROP TABLE IF EXISTS plop;
CREATE TABLE plop( firstname VARCHAR, lastname VARCHAR, username VARCHAR);
DROP FUNCTION IF EXISTS plip();
CREATE OR REPLACE FUNCTION plip()
RETURNS TRIGGER AS
$$
DECLARE
tmp VARCHAR default null;
cont integer default 0;
BEGIN
SELECT max(username) into tmp FROM plop where firstname = NEW.firstname and lastname = NEW.lastname;
if tmp is null then
NEW.username := NEW.firstname||'.'||NEW.lastname;
elsif trim(tmp, NEW.firstname||'.'||NEW.lastname) = '' then
cont := cont + 1;
NEW.username := NEW.firstname||'.'||NEW.lastname||cont::text;
else
cont := trim(tmp, NEW.firstname||'.'||NEW.lastname)::int + 1;
NEW.username := NEW.firstname||'.'||NEW.lastname||cont::text;
end if;
RETURN NEW;
END
$$
LANGUAGE PLPGSQL;
CREATE TRIGGER plop_insert
BEFORE INSERT OR UPDATE ON plop
FOR EACH ROW
EXECUTE PROCEDURE plip();
insert into plop (firstname, lastname) values ('Jean','Dupont');
insert into plop (firstname, lastname) values ('Jean','Dupont'),('Jean','Dupont');
INSERT INTO PLOP VALUES ('JEAN','DUPONT'),('JEAN','DUPONT'),('JEAN','DUPONT'),('JEAN','DURAND');
select * from plop;
Par contre, il me semble indispensable que tu gères les minuscules/majuscules, sinon, ça risque de poser des problème, à moins que tes login soient sensible à la casse.
Je n'ai pas essayer avec un \copy mais il n'y a pas trop de raisons que ça ne marche pas.
Cordialement
PS: Mon premier trigger, je suis ému
EDIT: il y a un bug, au dela de 10, il ne s'incrémente plus, qqn a une idée ?
EDIT2: Ca vient du fait que toto.toto9 est plus grand que toto.toto10 !
Oui, ca vient du fait que le trim ne renvoie rien (enfin si, un '') puisque dupon.toto n'a pas de suffixe, j'ai eu le tour hier, j'ai tenté de le résoudre avec une exception, mais sans succès .
Je suis en train d'essayer autre chose, mais ça ne fonctionne pas encore comme je le souhaite.
Cordialement
Bonsoir,
cela veut-il dire qu'en fait, je n'ai même pas besoin de defaut1 VARCHAR DEFAULT NULL; mais que defaut1 VARCHAR; suffirait ?
Cordialement
tout d'abord merci pour m'aider à résoudre mon souci. Je ne sais pas si j'ai mal expliqué, ce qui est fort possible, ou si je ne comprends pas comment mettre en application ton exemple pour mon cas.
En fait la valeur username n'est pas inséré par ma requête d'insert. Le username est généré automatiquement via la fonction trigger.CREATE TABLE plop( firstname VARCHAR, lastname VARCHAR, username VARCHAR);
INSERT INTO plop VALUES ('Jean','Dupont'),('Jean','Dupont'),('Jean','Dupont');en fait j'insère les données via un fichier csv où le username ne figure pas. Et donc le fait de faire générer automatiquement le username via le trigger serait super pour çà.
Ok, mais tu vas bien le stocker quelque part ce username généré pour chaque ligne ? Parce que sinon, je ne vois pas trop à quoi il va servir
Cordialement
Voila quelque chose qui fonctionne, mais qui devra être amélioré pour tenir compte notamment des minuscules/MAJUSCULES.
CREATE TABLE plop( firstname VARCHAR, lastname VARCHAR, username VARCHAR);
INSERT INTO plop VALUES ('Jean','Dupont','Jean.Dupont2'),('Jean','Dupont','Jean.Dupont1'),('Jean','Dupont','Jean.Dupont');
CREATE OR REPLACE FUNCTION plip(first VARCHAR, last VARCHAR)
RETURNS VARCHAR as
$$
DECLARE
tmp integer default 0;
BEGIN
SELECT trim(max(username),first||'.'||last)::int into tmp FROM plop where username like first||'.'||last||'%';
IF tmp > 0 THEN
tmp := tmp+1;
RETURN first||'.'||last||tmp::text;
ELSE
RETURN first||'.'||last;
END IF;
END;
$$
LANGUAGE PLPGSQL;
select plip('Jean','Durand');
select plip('Jean','Dupont');
Attention, c'est surement sub-optimal, je suis sur qu'il y a d'autres solutions plus élégantes !
Cordialement
PS : évite de coller ton code dans une image, il faut tout retaper pour faire des essais !
Bonjour,
dans un premier temps, je pense que le end if; avant le else if est de trop.
Par contre, que va t-il se passer lorsque on aura deja un jean.dupont et un jean.dupont1 ? on va réinsérer un jean.dupont1 de plus.
Cordialement
Bonjour,
j'avais fini par résoudre mon problème en incluant le nb de colonne renvoyé dans la table contenant les requêtes (la table qualito de mon premier message), et je traitais ma boucle sur les requête avec un CASE...WHEN pour gérer ce nombre de colonne retournées variables !
Mais, je trouvais que ce CASE...WHEN était lourd à gérer, j'ai donc réécris la fonction comme ceci:
CREATE OR REPLACE FUNCTION plop(datdeb date, datfin date)
RETURNS VOID AS
$BODY$
DECLARE
lstqry CURSOR FOR SELECT idqry, query, libqry FROM qualito;
qual qualito%ROWTYPE;
idnum INTEGER;
defaut1 VARCHAR DEFAULT NULL;
BEGIN
RAISE NOTICE 'Bornes des séjours : % à %', TO_CHAR(datdeb,'DD/MM/YYYY'),TO_CHAR(datfin,'DD/MM/YYYY');
FOR qual IN lstqry LOOP
RAISE NOTICE 'Exécution de la requête: % - %', qual.idqry, qual.libqry;
FOR idnum, defaut1 IN EXECUTE qual.query using datdeb, datfin LOOP
INSERT INTO result (idnum,iderr,defaut1) values (idnum,qual.idqry,defaut1);
END LOOP;
END LOOP;
END;
$BODY$
LANGUAGE PLPGSQL VOLATILE COST 100;
c'est a dire en defaultant à NULL la colonne qui n'est pas toujours renvoyée (defaut1) par les requêtes de la table qualito.
La question que je me posais était la suivante, quand je passe d'une requête qui a attribué une valeur à defaut1 à une requête qui ne renvoies que idnum et donc ne l'utilises pas, ne devrait-on pas s'attendre à ce que defaut1 garde la dernière valeur qui lui a été attribué ?
Ou comme apparemment ce n'est pas le cas, est-il resetté à NULL à chaque boucle sur ma table qualito ?
Cordialement
Je ne pense pas que ça soit possible.
Bonjour, comme c'est un peu calme aujourd'hui, voila le fruit de mes dernières cogitations et recherches à ce sujet:
On peut feinter en passant par un type prédéfini:
CREATE type nb_present_type AS (ddj DATE,etage VARCHAR, eff NUMERIC);
puis on le réutilise en faisant:
CREATE OR REPLACE FUNCTION nb_present(oneday DATE) RETURNS SETOF nb_present_type AS .....
et
CREATE OR REPLACE FUNCTION nb_present(datedeb DATE, datefin DATE) RETURNS SETOF nb_present_type AS .....
Cordialement
En tout cas, ce forum, ce n'est pas le bon endroit pour demander des nouvelles fonctionnalités
J'avais bien noté
Pour le subselect, certes il n'est pas la pour faire gagner du temps, mais le fait de ne pas avoir à écrire deux fois (et donc à calculer) certaines fonctions ou conditions aurait pu améliorer les choses.
Manifestement, c'est raté!
Cordialement
Oui, je viens d'aller voire la réponse de Tom, et je comprend bien que dans le cas qui etait posé, il ne puisse evaluer le résultat du select avant le where, sauf qu'en l'occurence, mes alias de colonnes ne sont pas dans la clause WHERE, mais dans la partie SELECT.
De la même manière, sur un ORDER BY ou un HAVING, on peut s'attendre à ce que ces clauses soient évaluées après le SELECT et partant de là, utiliser leurs alias (on y arrive bien avec un ORDER BY 1 (1 n'est t'il pas un alias de la première colonne ?)).
Au delà de la flemme d'écrire deux fois un "case" complexe (pas celui de l'exemple, hein), je voulais éviter de lui faire faire deux fois les calculs (et puis ça amèliore la lisibilité du code, quand on en modofie un, on est pas obliger d'aller faire la même chose sur l'autre, etc ...).
Après test avec une sous requête (comme proposé par Tom), je gagne presque rien!
Sur ma vraie requête, voila le résultat du subselect:
Sort (cost=29.89..30.39 rows=200 width=68) (actual time=22869.369..22869.441 rows=365 loops=1)
Sort Key: ss.ddj, ss.nbplaces
Sort Method: quicksort Memory: 42kB
-> Subquery Scan on ss (cost=15.25..22.25 rows=200 width=68) (actual time=22868.321..22869.102 rows=365 loops=1)
-> HashAggregate (cost=15.25..19.25 rows=200 width=36) (actual time=22868.315..22868.669 rows=365 loops=1)
-> Function Scan on nb_present (cost=0.25..10.25 rows=1000 width=36) (actual time=22850.656..22855.744 rows=17155 loops=1)
Total runtime: 22873.190 ms
et celui ou on réécrit deux fois les fonctions:
Sort (cost=32.39..32.89 rows=200 width=36) (actual time=22941.980..22942.053 rows=365 loops=1)
Sort Key: ddj, (CASE WHEN (date_part('isodow'::text, (ddj)::timestamp without time zone) > 5::double precision) THEN 705.0 ELSE 748.0 END)
Sort Method: quicksort Memory: 42kB
-> HashAggregate (cost=17.75..24.75 rows=200 width=36) (actual time=22940.959..22941.712 rows=365 loops=1)
-> Function Scan on nb_present (cost=0.25..10.25 rows=1000 width=36) (actual time=22923.321..22928.387 rows=17155 loops=1)
Total runtime: 22945.881 ms
Au delà du gain en performance pure, ne peut on pas imaginer que le developpemnt d'un telle fonctionalité serait un plus dans la facilité d'utilisation?
Cordialement
Bonjour,
mon problème du jour est le suivant, je souhaiterai réutiliser dans une même requête un alias de colonne (dans mes souvenir oraclien, c'était faisable) mais là, je n'y arrive pas;
Au lieu d'écrire ça:
select extract(isodow from '2011-01-11'::date) as joursem, case when extract(isodow from '2011-01-11'::date) > 5 then 700 else 500 end;
je voudrais pouvoir écrire ça
select extract(isodow from '2011-01-11'::date) as joursem, case when joursem > 5 then 700 else 500 end;
C'est également valable pour les "order" sur des alias de colonnes ou des "having".
Cordialement
Je pensais en avoir fini mais il y a une dernière chose qui m'intrigue. N'y aurait-il pas moyen de récupérer pour la definition de ma fonction nb_present(DATE,DATE) la type de retour de celle à une date ce qui me permettrai de modifier dynamiquement le type des valeurs renvoyées par celle à deux date en ne modifiant que celle a une date ?
genre Un truc du genre:
CREATE OR REPLACE FUNCTION nb_present(oneday DATE) RETURNS TABLE(ddj DATE,etage VARCHAR, eff NUMERIC) AS .....
et
CREATE OR REPLACE FUNCTION nb_present(datdeb DATE, datfin DATE) RETURNS nb_present(DATE)%ROWTYPE AS ....
C'est sur ce qu'il faudrait mettre à la place de nb_present(DATE)%ROWTYPE que je coince.
Enfin, STABLE, IMMUTABLE ? est-ce utile dans ce cas la ?
Cordialement
Bon, après avoir rajouté une colonne etage dans ma table d'essai, pour finir, voila a quoi j'arrive:
DROP FUNCTION IF EXISTS nb_present(DATE);
CREATE OR REPLACE FUNCTION nb_present(oneday DATE) RETURNS TABLE(ddj DATE,etage VARCHAR, eff NUMERIC) AS
$$
SELECT $1,etage,sum(case when ($1= depart and depart=arrivee) then 1
when $1=depart then 0.5
when $1=arrivee then 0.5
when ($1 > arrivee and $1 < depart) then 1
else 0 end)
from essai group by etage;
$$
LANGUAGE sql;
DROP FUNCTION IF EXISTS nb_present(DATE, DATE);
select * from nb_present('2001-01-01');
DROP FUNCTION IF EXISTS nb_present(DATE,DATE);
CREATE OR REPLACE FUNCTION nb_present(datdeb DATE, datfin DATE) RETURNS TABLE(ddj DATE,etage VARCHAR, eff NUMERIC) AS
$$
DECLARE
tmp DATE;
BEGIN
FOR tmp IN SELECT generate_series(datdeb, datfin,'1 day'::interval)
LOOP
RETURN QUERY SELECT * from nb_present(tmp);
END LOOP;
END
$$
LANGUAGE plpgsql;
ce qui me permet de faire ceci:
select * from nb_present('2011-01-01','2011-01-11');
ou ceci, si je ne veux pas le détail par étage:
select ddj, sum(eff) from nb_present('2011-01-01','2011-01-11') group by ddj order by 1;
Merci pour votre aide
Cordialement
Ok,
comme c'est pour un besoin ponctuel, la première méthode me va bien. L'avantage que je voyais dans l'agrégat était de faire des sous sélections rapidement. Du coup, je vais peut être faire les chose différemment, à savoir, prendre mon niveau le plus fin d'agrégat et faire des sommes lorsque je n'en ai pas besoin.
Merci de ton aide
Cordialement
Ne soit pas désolé, au fur et à mesure que je progresse, mes besoins de confort grandisse !
Dons, si je reprends mon exemple de départ (avec la table essai), je récupère bien mon nombre de présent pour un jour donné ou pour une période donnée.
Maintenant, admettons que j'ai un nouvelle colonne étage dans la table, si je veux connaitre le nb de personne par etage pour un jour donné, il va falloir que je refasse une fonction pour les compter.
Si j'ai un fonction d'aggrégat pour mes présents, je pourrait faire mes comptes indifférement sur la table complète, ou par étage, ou pas n'importe qu'elle autre critère de regroupement, seulement ça implique que je puisse écrire un truc du genre:
select nb_present('2011-01-02') from essai; -- pour tous les présent
select etage,nb_present('2011-01-02') from essai; -- pour avoir le décompte par étage
Mais ca implique que d'une part le nom de la table ne soit pas inscrit en dure dans la fonction (et probablement aussi le nom des colonnes correspondant à la date d'arrivée et de départ) mais qu'en plus ma fonction se comporte comme un aggrégat classique.
J'espère que je suis plus clair :-)
Cordialement
Ok, c'est noté.
Je vais profiter encore un peu de vos réponses éclairées.
L'idéal pour moi serait que j'arrive a avoir une fonction de type aggregat qui me permettre de segmenter les resultats par etage par exemple (si je
rajoutais une colonne étage à ma table essai) mais j'ai un peu du mal a conceptualiser la chose. Avez vous des liens ou je puisse voire des fonction aggrégat déclaré après coup dans postgresql, je suis sur que ça m'aiderai pas mal pour arriver à mes fins.
Je suis bien alleé voire CREATE AGGREGATE mais bon, c'est un peu abrupt quand on débute sur ce genre de chose.
Cordialement
Bonjour,
voila ou j'en suis avec mes histoires de présents à une date donnée.
J'ai commencé par créer une fonction pour me donner le nb de present à un date date donnée:
DROP FUNCTION IF EXISTS nb_present(DATE);
CREATE OR REPLACE FUNCTION nb_present(oneday DATE, OUT eff NUMERIC) AS
$$SELECT sum(case when ($1= depart and depart=arrivee) then 1
when $1=depart then 0.5
when $1=arrivee then 0.5
when ($1 > arrivee and $1 < depart) then 1
else 0 end)
from essai;
$$
LANGUAGE sql;
Puis, j'ai créé une seconde foncion (avec le même nom (est-ce mal ?)) qui fait la même chose pour un intervalle de date:
DROP FUNCTION IF EXISTS nb_present(DATE, DATE);
CREATE OR REPLACE FUNCTION nb_present(datdeb DATE, datfin DATE) RETURNS TABLE(ddj DATE, eff NUMERIC) AS
$$
DECLARE
BEGIN
FOR ddj IN SELECT generate_series(datdeb, datfin,'1 day'::interval)
LOOP
RETURN QUERY select ddj , nb_present(ddj);
END LOOP;
END
$$
LANGUAGE plpgsql;
et ça marche.
Mais, il y a un mais, je suis obligé de faire :
select * from nb_present('2011-01-01','2011-01-11')
pour obtenir le résulat sur deux colonnes. Y'a-t-il un moyen d'obtenir directement un affichage sur deux colonnes en faisant
select nb_present('2011-01-01','2011-01-11')
?
D'ailleurs en y pensant, les deux fonctions ne devrait elle pas renvoyer le même type de retour, à savoir date et effectif ?
Cordialement
Donc ma requête est bonne ! Non mais !
Oui, Marc, ta requête est bonne à un petit bemol près , si mon individu entre et sort le même jour, il a 0.5 jour de présence pour son entrée et 0.5 pour sa sortie (je sais, c'est tordu).
Rajoutons
insert into essai values ('durond','2011-01-02','2011-01-02');
Ce qui nous amène à :
PREPARE test (date) as
SELECT sum(case when ($1= depart and depart=arrivee) then 1
when $1=depart then 0.5
when $1=arrivee then 0.5
when ($1 > arrivee and $1 < depart) then 1
else 0 end)
from essai;
Cordialement
Bonjour,
les problèmes 1 et 3 étant résolus, il me reste règler mon soucis N°2, calculer le nombre de présents par jour sur une période donnée.
L'approche la plus efficace serait-t-elle de faire un boucle sur une le bout de code donné par Marc ?
Si oui, comment faire un generate_series sur des dates car manifestement, la fonction ne prend que des int ou bigint en paramètres (j'ai bien vu l'exemple de la doc avec un interval, mais je ne suis vraiment pas à l'aise avec ces trucs là).
Peut-on réutiliser le prepare tel quel, ou le fait de tout encapsulé dans une function règlerait-elle le problème en déclarant les variables ?
Cordialement
My pleasure !
Ok, par contre tu ne m'as pas dit en quoi le code que je t'avais fournit ne focntionnait pas ?
Y'a t-il un message d'erreur (si tu l'as exécuté dans psql, il devrait y avoir quelque chose)
Cordialement
Oooops, je n'ai vu le message de Marc qu'après (très pratique le "prepare", je vais le mettre sous le coude pour entretenir ma paresse naturelle)