2013-10-09 6 views
3

Une application web peut envoyer à une fonction d'un array of arrays commepassant un tableau de tableaux comme paramètre à une fonction

[ 
    [ 
     [1,2], 
     [3,4] 
    ], 
    [ 
     [], 
     [4,5,6] 
    ] 
] 

La longueur de la rangée extérieure est n > 0. Les tableaux du milieu sont de longueur constante, 2 dans cet exemple. Et les longueurs des matrices internes sont n >= 0.

Je string pourrais construire comme ceci:

with t(a, b) as (
    values (1, 4), (2, 3), (1, 4), (7, 3), (7, 4) 
) 
select distinct a, b 
from t 
where 
    (a = any(array[1,2]) or array_length(array[1,2],1) is null) 
    and 
    (b = any(array[3,4]) or array_length(array[3,4],1) is null) 
    or 
    (a = any(array[]::int[]) or array_length(array[]::int[],1) is null) 
    and 
    (b = any(array[4,5,6]) or array_length(array[4,5,6],1) is null) 
; 
a | b 
---+--- 
7 | 4 
1 | 4 
2 | 3 

Mais je pense que je peux faire mieux comme ça

with t(a, b) as (
    values (1, 4), (2, 3), (1, 4), (7, 3), (7, 4) 
), u as (
    select unnest(a)::text[] as a 
    from (values 
     (
      array[ 
       '{"{1,2}", "{3,4}"}', 
       '{"{}", "{4,5,6}"}' 
      ]::text[] 
     ) 
    ) s(a) 
), s as (
    select a[1]::int[] as a1, a[2]::int[] as a2 
    from u 
) 
select distinct a, b 
from 
    t 
    inner join 
    s on 
     (a = any(a1) or array_length(a1, 1) is null) 
     and 
     (b = any(a2) or array_length(a2, 1) is null) 
; 
a | b 
---+--- 
7 | 4 
2 | 3 
1 | 4 

avis qu'un text array a été adopté puis « casted » à l'intérieur de la fonction. Cela était nécessaire car Postgresql ne peut traiter que des tableaux de dimensions correspondantes et les tableaux internes transmis peuvent varier en dimension. Je pourrais les "réparer" avant de passer en ajoutant une valeur spéciale comme zéro pour les faire tous de la même longueur que le plus long mais je pense qu'il est plus propre de faire face à cela à l'intérieur de la fonction.

Ai-je raté quelque chose? Est-ce la meilleure approche?

+0

Le format du tableau tridimensionnel est-il enveloppé dans un format texte donné ou pouvez-vous choisir comment transmettre ces valeurs? Et la fonction recevant ces valeurs est plpgsql? Version Postgres? –

+0

@Erwin Je peux choisir comment transmettre les valeurs. L'extrémité avant est en Python donc très flexible. Ce format est juste comment je l'ai réussi à travailler. La fonction travaille maintenant en sql pour la simplicité mais elle peut être plpgsql ou plpython. Postgresql 9.3. –

Répondre

1

J'aime votre deuxième approche.

SELECT DISTINCT t.* 
FROM (VALUES (1, 4), (5, 1), (2, 3), (1, 4), (7, 3), (7, 4)) AS t(a, b) 
JOIN (
    SELECT arr[1]::int[] AS a1 
     ,arr[2]::int[] AS b1 
    FROM (
     SELECT unnest(ARRAY['{"{1,2}", "{3,4}"}' 
         ,'{"{}" , "{4,5,6}"}' 
         ,'{"{5}" , "{}"}' -- added element to 1st dimension 
         ])::text[] AS arr  -- 1d text array 
    ) sub 
    ) s ON (a = ANY(a1) OR a1 = '{}') 
     AND (b = ANY(b1) OR b1 = '{}') 
; 

améliorations mineures référentiel proposant uniquement:

  1. au lieu de sous-requêtes pour CTEs des performances légèrement meilleures.

  2. Test simplifié pour tableau vide: vérification par rapport au littéral '{}' au lieu de l'appel de fonction.

  3. Un niveau de sous-requête inférieur pour déballer la baie.

Résultat:

a | b 
--+--- 
2 | 3 
7 | 4 
1 | 4 
5 | 1 

Pour le lecteur occasionnel: l'emballage tableau multidimensionnel de nombre entier est nécessaire, car Postgres exige que (citant un message d'erreur):

tableaux multidimensionnels doit avoir des expressions de tableau avec des cotes correspondantes

Un autre itinéraire serait avec un tableau texte 2 dimensions et unnest à l'aide generate_subscripts():

WITH a(arr) AS (SELECT '{{"{1,2}", "{3,4}"} 
         ,{"{}", "{4,5,6}"} 
         ,{"{5}", "{}"}}'::text[] -- 2d text array 
      ) 
SELECT DISTINCT t.* 
FROM (VALUES (1, 4), (5, 1), (2, 3), (1, 4), (7, 3), (7, 4)) AS t(a, b) 
JOIN (
    SELECT arr[i][1]::int[] AS a1 
     ,arr[i][2]::int[] AS b1 
    FROM a, generate_subscripts(a.arr, 1) i  -- using implicit LATERAL 
    ) s ON (t.a = ANY(s.a1) OR s.a1 = '{}') 
     AND (t.b = ANY(s.b1) OR s.b1 = '{}'); 

peut être plus rapide, pouvez-vous tester?

Dans les versions antérieures à 9.3, on utiliserait explicitement CROSS JOIN à la place de la jonction croisée latérale.

+1

Oui, la version 'generate_subscripts' est toujours plus rapide dans la fonction réelle. Le temps d'exécution passe de 10 à 5 ms. –

Questions connexes