2013-07-08 10 views
9

Tworzę select, która łączy dwie tabele, zone i output, oparciu o odwołanie device stole i na odwzorowaniu zone_number do output_type_id. Mapowanie zone_number do output_type_id nie pojawia się w dowolnym miejscu w bazie danych, a ja chciałbym utworzyć go "w locie" w ramach wybranego oświadczenia . Poniżej jest mój schemat:Jak utworzyć tabelę mapowania „on-the-fly” w SELECT w PostgreSQL

CREATE TABLE output_type (
    id INTEGER NOT NULL, 
    name TEXT, 
    PRIMARY KEY (id) 
); 

CREATE TABLE device (
    id INTEGER NOT NULL, 
    name TEXT, 
    PRIMARY KEY (id) 
); 

CREATE TABLE zone (
    id SERIAL NOT NULL, 
    device_id INTEGER NOT NULL REFERENCES device(id), 
    zone_number INTEGER NOT NULL, 
    PRIMARY KEY (id), 
    UNIQUE (zone_number) 
); 

CREATE TABLE output (
    id SERIAL NOT NULL, 
    device_id INTEGER NOT NULL REFERENCES device(id), 
    output_type_id INTEGER NOT NULL REFERENCES output_type(id), 
    enabled BOOLEAN NOT NULL, 
    PRIMARY KEY (id) 
); 

I Oto przykład dane:

INSERT INTO output_type (id, name) VALUES 
(101, 'Output 1'), 
(202, 'Output 2'), 
(303, 'Output 3'), 
(404, 'Output 4'); 

INSERT INTO device (id, name) VALUES 
(1, 'Test Device'); 

INSERT INTO zone (device_id, zone_number) VALUES 
(1, 1), 
(1, 2), 
(1, 3), 
(1, 4); 

INSERT INTO output (device_id, output_type_id, enabled) VALUES 
(1, 101, TRUE), 
(1, 202, FALSE), 
(1, 303, FALSE), 
(1, 404, TRUE); 

muszę pobrać powiązany enabled pole z tabeli wyjściowej dla każdej strefy dla danego urządzenia. Każdy zone_number mapuje na output_type_id. Na tym przykładzie:

zone_number | output_type_id 
---------------------------- 
1   | 101 
2   | 202 
3   | 303 
4   | 404 

jeden sposób, aby obsłużyć mapowanie byłoby utworzyć nową tabelę

CREATE TABLE zone_output_type_map (
    zone_number INTEGER, 
    output_type_id INTEGER NOT NULL REFERENCES output_type(id) 
); 

INSERT INTO zone_output_type_map (zone_number, output_type_id) VALUES 
(1, 101), 
(2, 202), 
(3, 303), 
(4, 404); 

i używać następujące SQL, aby uzyskać wszystkie strefy, oraz flag enabled dla urządzenia 1:

SELECT zone.*, output.enabled 
FROM zone 
JOIN output 
ON output.device_id = zone.device_id 
JOIN zone_output_type_map map 
ON map.zone_number = zone.zone_number 
AND map.output_type_id = output.output_type_id 
AND zone.device_id = 1 

Jednakże szukam sposób, aby utworzyć odwzorowanie nunbers strefy do wyjścia typy bez tworzenia nowej tabeli i bez zszywania pęczek a Instrukcje ND/OR . Czy istnieje elegancki sposób tworzenia mapowania między dwoma polami w instrukcji select? Coś jak:

SELECT zone.*, output.enabled 
FROM zone 
JOIN output 
ON output.device_id = zone.device_id 
JOIN (
    SELECT (
     1 => 101, 
     2 => 202, 
     3 => 303, 
     4 => 404 
    ) (zone_number, output_type_id) 
) as map 
ON map.zone_number = zone.zone_number 
AND map.output_type_id = output.output_type_id 
AND zone.device_id = 1 

Disclaimer: wiem, że idealnie pole enabled istniałby w tabeli zone . Jednak nie mam kontroli nad tym kawałkiem. Po prostu szukam najbardziej eleganckiego rozwiązania od strony aplikacji. Dzięki!

+0

Może mógłbyś użyć WIDOKU? –

+0

Byłbym w porządku z tym, ale podstawowe mapowanie nadal będzie musiało być zdefiniowane w instrukcji select (w widoku), i to tam się dla mnie załamuje. Wygląda na to, że powinien istnieć prosty sposób tworzenia klucza "hash", który może być użyty w instrukcji select do dołączenia do dwóch tabel. – Divey

+0

Twoje przykładowe dane 'output_type' nie pasują do twojego' wyjścia. output_type_id', pierwszy używa 101, 102, 103 i 104, podczas gdy drugi używa 101, 202, 303 i 404; wuj dla FK za wskazanie mi tego :) –

Odpowiedz

24

Można użyć VALUES jako tabeli inline i dołącz do niego, wystarczy, aby nadać jej aliasem i kolumn nazwy:

join (values (1, 101), (2, 202), (3, 303), (4, 304)) as map(zone_number, output_type_id) 
on ... 

Z fine manual:

VALUES może być również używany, gdy można napisać sub-SELECT dla przykładu w klauzuli FROM:

SELECT f.* 
    FROM films f, (VALUES('MGM', 'Horror'), ('UA', 'Sci-Fi')) AS t (studio, kind) 
    WHERE f.studio = t.studio AND f.kind = t.kind; 

UPDATE employees SET salary = salary * v.increase 
    FROM (VALUES(1, 200000, 1.2), (2, 400000, 1.4)) AS v (depno, target, increase) 
    WHERE employees.depno = v.depno AND employees.sales >= v.target; 
+0

Idealnie! To pasuje do rachunku. – Divey

+2

Piękne! Możesz nawet zdefiniować aliasy dla kolumn w zestawie wyników! –

-1
JOIN 
(SELECT 1 zone_number, 101 as output_type_id 
UNION ALL 
SELECT 2 zone_number, 202 as output_type_id 
UNION ALL 
SELECT 3 zone_number, 303 as output_type_id 
) mappings on mappings.zone_number = zone.zone_number 
0

Więc po prostu uzupełnienie przyjętej odpowiedź, następujący kod jest prawidłowy, samowystarczalny Postgresql wyrażenie, które oceni do „inline” relacji z kolumnami (zone_number, output_type_id):

SELECT * FROM 
(VALUES 
    (1, 101), 
    (2, 202), 
    (3, 303), 
    (4, 304) 
) as i(zone_number, output_type_id) 

(Tylko część (VALUES ... AS ...) nie będzie miała poprawnego wyrażenia, dlatego dodałem SELECT * FROM.)

Powiązane problemy