Actualizar SQL y unir tres tablas según las filas de una tabla y no en otra

I have a bit of a complicated sql query I need to do, and I'm a bit stuck. I'm using SQLite if that changes anything.

Tengo la siguiente estructura de tabla:

Table G
---------
G_id (primary key) | Other cols ...
====================================
        21
        22
        23
        24
        25
        26
        27 (no g_to_s_map)
        28 

.

Table S
---------
S_id (primary key) |  S_num  | Other cols.....
====================================
        1              1101
        2              1102
        3              1103
        4              1104
        5              1105
        6              1106
        7              1107      (no g_to_s_map, no s_to_t_map)
        8              1108      (no g_to_s_map, there IS an s_to_t_map)
        9              1109      (there is an g_to_s_map, but no s_to_t map)

.

Table T
---------
T_id (primary key) | Other cols...
==================================
        1
        2

Then I also have two mapping tables:

Table G_to_S_Map (1:1 mapping, unique values of both g_id and s_id)
----------
G_id (foreign key ref g)| S_id (foreign key ref s)
===================================================
           21                        1  
           22                        2  
           23                        3  
           24                        4  
           25                        5  
           26                        6  
           28                        9

.

Table S_to_T_Map (many:1 mapping, many unique s_id to a t_id)
----------
S_id (foreign key ref s) | T_id (foreign key ref s)
===================================================
           1                         1    
           2                         1    
           3                         1    
           4                         2    
           5                         2    
           6                         2 
           8                         2

Dado solo un T_id y un G_id, I need to be able to update the G_to_S_Map con el primero S_id correspondiente al especificado T_id (en el S_to_T_Map) that is NOT in the G_to_S_Map

The first thing I was thinking of was just getting any S_id's that corresponded to the T_id en el objeto S_to_T_Map:

SELECT S_id FROM S_to_T_Map where T_id = GIVEN_T_ID;

Then presumably I would join those values somehow with the G_to_S_Map using a left/right join maybe, and then look for the first value which doesn't exist on one of the sides? Then I'd need to do an insert into the G_to_S_Map basado en eso S_id y GIVEN_G_ID value or something.

Any suggestions on how to go about this? Thanks!


Edit: Added some dummy data:

preguntado el 09 de marzo de 12 a las 16:03

2 Respuestas

Creo que esto debería funcionar:

INSERT INTO G_To_S_Map (G_id, S_id) 
          (SELECT :inputGId, a.S_id
           FROM S_To_T_Map as a
           LEFT JOIN G_To_S_Map as b
           ON b.S_id = a.S_id
           AND b.G_id = :inputGId
           WHERE a.T_id = :inputTId
           AND b.G_id IS NULL
           ORDER BY a.S_id
           LIMIT 1);


EDIT:

If you're wanting to do the order by a different table, use this version:

INSERT INTO G_To_S_Map (G_id, S_id) 
          (SELECT :inputGId, a.S_id
           FROM S_To_T_Map as a
           JOIN S as b
           ON b.S_id = a.S_id
           LEFT JOIN G_To_S_Map as c
           ON c.S_id = a.S_id
           AND c.G_id = :inputGId
           WHERE a.T_id = :inputTId
           AND c.G_id IS NULL
           ORDER BY b.S_num
           LIMIT 1);

(As an aside, I really hope your tables aren't Realmente named like this, because that's a terrible thing to do. The use of Map, especially, should probablemente be avoided)


EDIT:

Here's some example test data. Have I missed something? Did I conceptualize the relationships incorrectly?

S_To_T_Map
================
S_ID    T_ID    
   1       1    
   2       1    
   3       1    
   1       2    
   1       3    
   3       3 

G_To_S_Map
==================   
G_ID    S_ID  
   1       1  
   3       1  
   2       1  
   3       2  
   2       3  
   3       3  

Resulting joined data:
(CTEs used to generate cross-join test data)

Results:
=============================
G_TEST    T_TEST    S_ID 
     1         1       3 
     2         1       2 
     1         3       3 


EDIT:

Ah, okay, now I get the problem. My issue was that I was assuming there was some sort of many-one relationship between S y G. As this is not the case, use this amended statement:

INSERT INTO G_To_S_Map (G_id, S_id)  
      (SELECT :inputGId, a.S_id 
       FROM S_To_T_Map as a 
       JOIN S as b 
       ON b.S_id = a.S_id 
       LEFT JOIN G_To_S_Map as c 
       ON c.S_id = a.S_id 
       OR c.G_id = :inputGId
       WHERE a.T_id = :inputTId 
       AND c.G_id IS NULL 
       ORDER BY b.S_num 
       LIMIT 1); 

Specficially, the line checking G_To_S_Map for a row containing the G_Id needed to be switched from using an AND a una OR - the key requirement which had not been specified previously was the fact that both G_Id y S_Id were unique in G_To_S_Map.
This statement will not insert a line if either the provided G_Id has been mapped previously, or if all S_Ids mapped to the given T_Id have been mapped.

respondido 12 mar '12, 18:03

Interesting. Thanks! Why is there an :inputGId in the select of the S_to_T_Map? I assume yours is more efficient than my multiple selects. What if I wanted to ORDER BY a field called "s_Num" in table "S" instead, though? - Jordania

Estoy usando el :<variableName> as host variables (I don't know what your syntax looks like). The reason I use it in the SELECT portion is that I'm expecting the resulting references of G_To_S_Map para ser nulo - there won't be a reference from a table to use. Answer edited to include reference to table S - Musa Mecánica

Ah, thanks! Yeah, those are just sanitized variable names, I'd be crazy to use those otherwise :p - Jordania

Just wondering, what is this line: "ON s.id = a.S_id" ? Not sure what s.id was supposed to be. Probably b.s_id though. Also, "cb.G_id = :inputGId" did you mean "c.G_id"? These variable names are brutal, my bad ;) - Jordania

It doesn't appear to work, unforuntately. I get a value that's already in the "g_to_s_map" when I run it with the above changes. It's just giving me the value of the one with the lowest S_num, regardless of whether or not it's in the table. - Jordania

Hmm, the following seems to work nicely, although I haven't combined an "insert" with it yet.

Select s.S_ID from S as s 
inner join(
    Select st.S_ID from s_to_t_map as st 
    where st.T_ID=???? AND not exists
                    (Select * from g_to_s_Map as gs where gs.S_ID = st.S_ID)
 ) rslt on s.S_ID=rslt.S_ID  ORDER BY s.s_Num ASC limit 1;

respondido 09 mar '12, 17:03

Sin una ORDER BY, you're getting a 'random' row. And stop using 'update', which has a specific meaning in SQL. Tagging out to table S is pointless, as I would assume you have the necessary fk references. - Musa Mecánica

Yeah, I have an ORDER BY in my syntax, just took it out for the purposes of this. And yeah, I meant "insert" and not "update", forgot that the entry wouldn't have existed yet. - Jordania

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.