Ricerca…


introduzione

JOIN è un metodo per combinare (unire) informazioni da due tabelle. Il risultato è un insieme di colonne cucite da entrambe le tabelle, definite dal tipo di join (INTERNO / ESTERNO / CROCE e SINISTRA / DESTRA / PIENA, spiegato di seguito) e criteri di unione (come si riferiscono le righe di entrambe le tabelle).

Una tabella può essere unita a se stessa oa qualsiasi altra tabella. Se è necessario accedere a informazioni da più di due tabelle, è possibile specificare più join in una clausola FROM.

Sintassi

  • [ { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } ] JOIN

Osservazioni

I join, come suggerisce il loro nome, sono un modo per interrogare i dati di più tabelle in modo congiunto, con le righe che mostrano colonne prese da più di una tabella.

Giuntura interna esplicita di base

Un join di base (chiamato anche "inner join") esegue query sui dati da due tabelle, con la loro relazione definita in una clausola join .

L'esempio seguente selezionerà i nomi dei dipendenti (FName) dalla tabella Impiegati e il nome del reparto per cui lavorano (Nome) dalla tabella Departments:

SELECT Employees.FName, Departments.Name
FROM   Employees
JOIN   Departments
ON Employees.DepartmentId = Departments.Id

Ciò restituirebbe il seguente dal database di esempio :

Employees.FName Departments.Name
Giacomo HR
John HR
Richard I saldi

Unirsi implicitamente

Join possono anche essere eseguite da avere più tabelle nel from clausola separati da virgole , e definisce il rapporto tra loro in where clausola. Questa tecnica è chiamata un'adesione implicita (poiché in realtà non contiene una clausola join ).

Tutti gli RDBMS lo supportano, ma di solito la sintassi viene sconsigliata. I motivi per cui è una cattiva idea usare questa sintassi sono:

  • È possibile ottenere crossover accidentali che restituiscono risultati errati, soprattutto se si hanno molti join nella query.
  • Se intendevi un cross join, allora non è chiaro dalla sintassi (scrivi invece CROSS JOIN), e qualcuno potrebbe cambiarlo durante la manutenzione.

Nell'esempio seguente verranno selezionati i nomi dei dipendenti e il nome dei reparti per cui lavorano:

SELECT e.FName, d.Name
FROM   Employee e, Departments d
WHERE  e.DeptartmentId = d.Id

Ciò restituirebbe il seguente dal database di esempio :

d.nome d.Name
Giacomo HR
John HR
Richard I saldi

Left Outer Join

Un Join esterno sinistro (noto anche come Join sinistro o Join esterno) è un Join che assicura che tutte le righe della tabella sinistra siano rappresentate; se non esiste una riga corrispondente dalla tabella di destra, i suoi campi corrispondenti sono NULL .

L'esempio seguente selezionerà tutti i reparti e il primo nome dei dipendenti che lavorano in quel dipartimento. I reparti senza dipendenti sono ancora restituiti nei risultati, ma avranno NULL per il nome del dipendente:

SELECT          Departments.Name, Employees.FName
FROM            Departments 
LEFT OUTER JOIN Employees 
ON              Departments.Id = Employees.DepartmentId

Ciò restituirebbe il seguente dal database di esempio :

Departments.Name Employees.FName
HR Giacomo
HR John
HR Johnathon
I saldi Michael
Tech NULLO

Quindi come funziona?

Ci sono due tabelle nella clausola FROM:

Id FName LName Numero di telefono ManagerID DepartmentID Stipendio Data di assunzione
1 Giacomo fabbro 1234567890 NULLO 1 1000 01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005
3 Michael Williams 1357911131 1 2 600 12-05-2009
4 Johnathon fabbro 1212121212 2 1 500 24-07-2016

e

Id Nome
1 HR
2 I saldi
3 Tech

Innanzitutto un prodotto cartesiano viene creato dalle due tabelle dando una tabella intermedia.
I record che soddisfano i criteri di join ( Departments.Id = Employees.DepartmentId ) sono evidenziati in grassetto; questi sono passati alla fase successiva della query.

Poiché questo è un GIÙ ESTERNO SINISTRO, tutti i record vengono restituiti dal lato SINISTRA del join (Dipartimenti), mentre a tutti i record sul lato DESTRO viene assegnato un indicatore NULL se non corrispondono ai criteri di join. Nella tabella seguente, questo restituirà Tech con NULL

Id Nome Id FName LName Numero di telefono ManagerID DepartmentID Stipendio Data di assunzione
1 HR 1 Giacomo fabbro 1234567890 NULLO 1 1000 01-01-2002
1 HR 2 John Johnson 2468101214 1 1 400 23-03-2005
1 HR 3 Michael Williams 1357911131 1 2 600 12-05-2009
1 HR 4 Johnathon fabbro 1212121212 2 1 500 24-07-2016
2 I saldi 1 Giacomo fabbro 1234567890 NULLO 1 1000 01-01-2002
2 I saldi 2 John Johnson 2468101214 1 1 400 23-03-2005
2 I saldi 3 Michael Williams 1357911131 1 2 600 12-05-2009
2 I saldi 4 Johnathon fabbro 1212121212 2 1 500 24-07-2016
3 Tech 1 Giacomo fabbro 1234567890 NULLO 1 1000 01-01-2002
3 Tech 2 John Johnson 2468101214 1 1 400 23-03-2005
3 Tech 3 Michael Williams 1357911131 1 2 600 12-05-2009
3 Tech 4 Johnathon fabbro 1212121212 2 1 500 24-07-2016

Infine, ogni espressione utilizzata all'interno della clausola SELECT viene valutata per restituire il nostro tavolo finale:

Departments.Name Employees.FName
HR Giacomo
HR John
I saldi Richard
Tech NULLO

Self Join

Una tabella può essere unita a se stessa, con righe diverse che si corrispondono a seconda delle condizioni. In questo caso d'uso, è necessario utilizzare alias per distinguere le due occorrenze della tabella.

Nell'esempio seguente, per ciascun Dipendente nella tabella Dipendenti del database di esempio , viene restituito un record contenente il nome del dipendente insieme al nome corrispondente corrispondente del manager del dipendente. Poiché i manager sono anche dipendenti, la tabella è unita a se stessa:

SELECT 
    e.FName AS "Employee", 
    m.FName AS "Manager"
FROM   
    Employees e
JOIN   
    Employees m 
    ON e.ManagerId = m.Id

Questa query restituirà i seguenti dati:

Dipendente Manager
John Giacomo
Michael Giacomo
Johnathon John

Quindi come funziona?

La tabella originale contiene questi record:

Id FName LName Numero di telefono ManagerID DepartmentID Stipendio Data di assunzione
1 Giacomo fabbro 1234567890 NULLO 1 1000 01-01-2002
2 John Johnson 2468101214 1 1 400 23-03-2005
3 Michael Williams 1357911131 1 2 600 12-05-2009
4 Johnathon fabbro 1212121212 2 1 500 24-07-2016

La prima azione consiste nel creare un prodotto cartesiano di tutti i record nelle tabelle utilizzate nella clausola FROM . In questo caso è la tabella Dipendenti due volte, quindi la tabella intermedia sarà simile a questa (ho rimosso tutti i campi non utilizzati in questo esempio):

e.Id d.nome e.ManagerId m.Id m.FName m.ManagerId
1 Giacomo NULLO 1 Giacomo NULLO
1 Giacomo NULLO 2 John 1
1 Giacomo NULLO 3 Michael 1
1 Giacomo NULLO 4 Johnathon 2
2 John 1 1 Giacomo NULLO
2 John 1 2 John 1
2 John 1 3 Michael 1
2 John 1 4 Johnathon 2
3 Michael 1 1 Giacomo NULLO
3 Michael 1 2 John 1
3 Michael 1 3 Michael 1
3 Michael 1 4 Johnathon 2
4 Johnathon 2 1 Giacomo NULLO
4 Johnathon 2 2 John 1
4 Johnathon 2 3 Michael 1
4 Johnathon 2 4 Johnathon 2

L'azione successiva è quella di mantenere solo i record che soddisfano i criteri di unire, in modo che qualsiasi record in cui il alias e tavolo ManagerId uguale al alias m tavolo Id :

e.Id d.nome e.ManagerId m.Id m.FName m.ManagerId
2 John 1 1 Giacomo NULLO
3 Michael 1 1 Giacomo NULLO
4 Johnathon 2 2 John 1

Quindi, ciascuna espressione utilizzata all'interno della clausola SELECT viene valutata per restituire questa tabella:

d.nome m.FName
John Giacomo
Michael Giacomo
Johnathon John

Infine, i nomi delle colonne e.FName e m.FName sono sostituiti dai loro nomi di colonne di alias, assegnati con l'operatore AS :

Dipendente Manager
John Giacomo
Michael Giacomo
Johnathon John

CROSS JOIN

Cross join fa un prodotto cartesiano dei due membri, un prodotto cartesiano significa che ogni riga di una tabella è combinata con ciascuna riga della seconda tabella nel join. Ad esempio, se TABLEA ha 20 righe e TABLEB ha 20 righe, il risultato sarà 20*20 = 400 righe di output.

Utilizzo del database di esempio

SELECT d.Name, e.FName
FROM   Departments d
CROSS JOIN Employees e;

Che restituisce:

d.Name d.nome
HR Giacomo
HR John
HR Michael
HR Johnathon
I saldi Giacomo
I saldi John
I saldi Michael
I saldi Johnathon
Tech Giacomo
Tech John
Tech Michael
Tech Johnathon

Si consiglia di scrivere un CROSS JOIN esplicito se si vuole fare un join cartesiano, per evidenziare che questo è ciò che si desidera.

Partecipare a una sottoquery

Unire una sottoquery viene spesso utilizzato quando si desidera ottenere dati aggregati da una tabella figlio / dettagli e visualizzarli insieme ai record dalla tabella padre / intestazione. Ad esempio, è possibile che si desideri ottenere un conteggio dei record figlio, una media di alcune colonne numeriche nei record figlio o la riga superiore o inferiore in base a una data o un campo numerico. In questo esempio vengono utilizzati alias, il che rende più facile leggere le query quando sono coinvolte più tabelle. Ecco come appare un join di subquery piuttosto tipico. In questo caso, recuperiamo tutte le righe dalla tabella padre Ordini d'acquisto e recuperiamo solo la prima riga per ogni record padre della tabella figlio PurchaseOrderLineItems.

SELECT po.Id, po.PODate, po.VendorName, po.Status, item.ItemNo, 
  item.Description, item.Cost, item.Price
FROM PurchaseOrders po
LEFT JOIN 
     (
       SELECT l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price, Min(l.id) as Id 
       FROM PurchaseOrderLineItems l
       GROUP BY l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price
     ) AS item ON item.PurchaseOrderId = po.Id

CROSS APPLY & LATERAL JOIN

Un tipo molto interessante di JOIN è il LATERAL JOIN (nuovo in PostgreSQL 9.3+),
che è anche conosciuto come APPLICAZIONE CROSS / APPLICAZIONE ESTERNA in SQL-Server e Oracle.

L'idea di base è che una funzione valutata a livello di tabella (o subquery inline) venga applicata per ogni riga che aggiungi.

Ciò consente, ad esempio, di unire solo la prima voce corrispondente in un'altra tabella.
La differenza tra un join normale e uno laterale risiede nel fatto che puoi utilizzare una colonna che hai precedentemente aggiunto alla sottoquery che hai "CROSS APPLY".

Sintassi:

PostgreSQL 9.3+

sinistra | giusto | inner JOIN LATERAL

Server SQL:

CROCE | DOMANDA ESTERNA

INNER JOIN LATERAL è uguale a CROSS APPLY
e LEFT JOIN LATERAL è lo stesso di OUTER APPLY

Esempio di utilizzo (PostgreSQL 9.3+):

SELECT * FROM T_Contacts 

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989


LEFT JOIN LATERAL 
(
    SELECT 
         --MAP_CTCOU_UID    
         MAP_CTCOU_CT_UID   
        ,MAP_CTCOU_COU_UID  
        ,MAP_CTCOU_DateFrom 
        ,MAP_CTCOU_DateTo   
   FROM T_MAP_Contacts_Ref_OrganisationalUnit 
   WHERE MAP_CTCOU_SoftDeleteStatus = 1 
   AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID 

    /*  
    AND 
    ( 
        (__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) 
        AND 
        (__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) 
    ) 
    */
   ORDER BY MAP_CTCOU_DateFrom 
   LIMIT 1 
) AS FirstOE 

E per SQL-Server

SELECT * FROM T_Contacts 

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989

-- CROSS APPLY -- = INNER JOIN 
OUTER APPLY    -- = LEFT JOIN 
(
    SELECT TOP 1 
         --MAP_CTCOU_UID    
         MAP_CTCOU_CT_UID   
        ,MAP_CTCOU_COU_UID  
        ,MAP_CTCOU_DateFrom 
        ,MAP_CTCOU_DateTo   
   FROM T_MAP_Contacts_Ref_OrganisationalUnit 
   WHERE MAP_CTCOU_SoftDeleteStatus = 1 
   AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID 

    /*  
    AND 
    ( 
        (@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) 
        AND 
        (@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) 
    ) 
    */
   ORDER BY MAP_CTCOU_DateFrom 
) AS FirstOE 

FULL JOIN

Un tipo di JOIN meno conosciuto è FULL JOIN.
(Nota: FULL JOIN non è supportato da MySQL come da 2016)

Un FULL OUTER JOIN restituisce tutte le righe dalla tabella di sinistra e tutte le righe dalla tabella di destra.

Se nella tabella a sinistra ci sono delle righe che non hanno corrispondenze nella tabella di destra, o se ci sono delle righe nella tabella di destra che non hanno corrispondenze nella tabella di sinistra, anche quelle righe saranno elencate.

Esempio 1 :

SELECT * FROM Table1

FULL JOIN Table2 
     ON 1 = 2 

Esempio 2:

SELECT 
     COALESCE(T_Budget.Year, tYear.Year) AS RPT_BudgetInYear 
    ,COALESCE(T_Budget.Value, 0.0) AS RPT_Value 
FROM T_Budget 

FULL JOIN tfu_RPT_All_CreateYearInterval(@budget_year_from, @budget_year_to) AS tYear 
      ON tYear.Year = T_Budget.Year 

Si noti che se si utilizzano eliminazioni software, sarà necessario controllare nuovamente lo stato di eliminazione software nella clausola WHERE (poiché FULL JOIN si comporta in modo simile a UNION);
È facile trascurare questo piccolo fatto, dal momento che hai inserito AP_SoftDeleteStatus = 1 nella clausola join.

Inoltre, se stai facendo un FULL JOIN, di solito devi consentire NULL nella clausola WHERE; dimenticarsi di consentire a NULL su un valore avrà gli stessi effetti di un join INNER, che è qualcosa che non vuoi se stai facendo un FULL JOIN.

Esempio:

SELECT 
     T_AccountPlan.AP_UID
    ,T_AccountPlan.AP_Code
    ,T_AccountPlan.AP_Lang_EN
    ,T_BudgetPositions.BUP_Budget
    ,T_BudgetPositions.BUP_UID 
    ,T_BudgetPositions.BUP_Jahr
FROM T_BudgetPositions    

FULL JOIN T_AccountPlan
    ON T_AccountPlan.AP_UID = T_BudgetPositions.BUP_AP_UID 
    AND T_AccountPlan.AP_SoftDeleteStatus = 1 

WHERE (1=1) 
AND (T_BudgetPositions.BUP_SoftDeleteStatus = 1 OR T_BudgetPositions.BUP_SoftDeleteStatus IS NULL) 
AND (T_AccountPlan.AP_SoftDeleteStatus = 1 OR T_AccountPlan.AP_SoftDeleteStatus IS NULL) 

JOIN ricorsivi

I join ricorsivi vengono spesso utilizzati per ottenere dati padre-figlio. In SQL, sono implementati con espressioni di tabella comuni ricorsive, ad esempio:

WITH RECURSIVE MyDescendants AS (
    SELECT Name
    FROM People
    WHERE Name = 'John Doe'

    UNION ALL

    SELECT People.Name
    FROM People
    JOIN MyDescendants ON People.Name = MyDescendants.Parent
)
SELECT * FROM MyDescendants;

Differenze tra join interni / esterni

SQL ha vari tipi di join per specificare se le righe (non) corrispondenti sono incluse nel risultato: INNER JOIN , LEFT OUTER JOIN , RIGHT OUTER JOIN e FULL OUTER JOIN (le parole chiave INNER e OUTER sono facoltative). La figura seguente evidenzia le differenze tra questi tipi di join: l'area blu rappresenta i risultati restituiti dal join e l'area bianca rappresenta i risultati che il join non restituirà.

Diagrammi di Venn che rappresentano i join interni / esterni di SQL

Presentazione incrociata di SQL ( riferimento ):

inserisci la descrizione dell'immagine qui

Di seguito sono riportati esempi di questa risposta.

Ad esempio ci sono due tabelle come di seguito:

A    B
-    -
1    3
2    4
3    5
4    6

Nota che (1,2) sono unici per A, (3,4) sono comuni e (5,6) sono unici per B.

Join interno

Un join interno che utilizza una delle query equivalenti fornisce l'intersezione delle due tabelle, ovvero le due righe che hanno in comune:

select * from a INNER JOIN b on a.a = b.b;
select a.*,b.* from a,b where a.a = b.b;

a | b
--+--
3 | 3
4 | 4

Giuntura esterna sinistra

Un join esterno sinistro darà tutte le righe in A, più eventuali righe comuni in B:

select * from a LEFT OUTER JOIN b on a.a = b.b;

a |  b
--+-----
1 | null
2 | null
3 |    3
4 |    4

Giuntura esterna destra

Allo stesso modo, un join esterno destro darà tutte le righe in B, più tutte le righe comuni in A:

select * from a RIGHT OUTER JOIN b on a.a = b.b;

a    |  b
-----+----
3    |  3
4    |  4
null |  5
null |  6

Full outer join

Un join esterno completo ti darà l'unione di A e B, cioè tutte le righe in A e tutte le righe in B. Se qualcosa in A non ha un dato corrispondente in B, allora la parte B è nullo, e vice versa.

select * from a FULL OUTER JOIN b on a.a = b.b;

 a   |  b
-----+-----
   1 | null
   2 | null
   3 |    3
   4 |    4
null |    6
null |    5

TERMINOLOGIA: Interno, Esterno, Semi, Anti ...

Diciamo che abbiamo due tabelle (A e B) e alcune delle loro righe corrispondono (relative alla condizione JOIN data, qualunque essa sia nel caso particolare):

Unisciti alla panoramica sulla terminologia

Possiamo utilizzare vari tipi di join per includere o escludere righe corrispondenti o non corrispondenti da entrambi i lati e denominare correttamente il join selezionando i termini corrispondenti dal diagramma precedente.

Gli esempi seguenti utilizzano i seguenti dati di test:

CREATE TABLE A (
    X varchar(255) PRIMARY KEY
);

CREATE TABLE B (
    Y varchar(255) PRIMARY KEY
);

INSERT INTO A VALUES
    ('Amy'),
    ('John'),
    ('Lisa'),
    ('Marco'),
    ('Phil');

INSERT INTO B VALUES
    ('Lisa'),
    ('Marco'),
    ('Phil'),
    ('Tim'),
    ('Vincent');

Join interno

Combina le righe sinistra e destra che corrispondono.

Join interno

SELECT * FROM A JOIN B ON X = Y;

X      Y
------ -----
Lisa   Lisa
Marco  Marco
Phil   Phil

Left Outer Join

A volte abbreviato in "left join". Combina le righe sinistra e destra che corrispondono e include le righe non corrispondenti a sinistra.

Left Outer Join

SELECT * FROM A LEFT JOIN B ON X = Y;

X      Y
-----  -----
Amy    NULL
John   NULL
Lisa   Lisa
Marco  Marco
Phil   Phil

Giusto outer join

A volte abbreviato in "right join". Combina le righe sinistra e destra che corrispondono, e include le righe a destra non corrispondenti.

Giusto outer join

SELECT * FROM A RIGHT JOIN B ON X = Y;

X      Y
-----  -------
Lisa   Lisa
Marco  Marco
Phil   Phil
NULL   Tim
NULL   Vincent

Full Outer Join

A volte abbreviato in "full join". Unione di giuntura esterna sinistra e destra.

Full Outer Join

SELECT * FROM A FULL JOIN B ON X = Y;

X      Y
-----  -------
Amy    NULL
John   NULL
Lisa   Lisa
Marco  Marco
Phil   Phil
NULL   Tim
NULL   Vincent

Left Semi Join

Include le righe a sinistra che corrispondono alle righe a destra.

Left Semi Join

SELECT * FROM A WHERE X IN (SELECT Y FROM B);

X
-----
Lisa
Marco
Phil

Right Semi Join

Include le righe giuste che corrispondono alle righe a sinistra.

Right Semi Join

SELECT * FROM B WHERE Y IN (SELECT X FROM A);

Y
-----
Lisa
Marco
Phil

Come puoi vedere, non esiste una sintassi IN dedicata per la semina sinistra e destra - otteniamo l'effetto semplicemente cambiando le posizioni della tabella all'interno del testo SQL.


Sinistra Anti Semi Join

Include le righe a sinistra che non corrispondono alle righe giuste.

Sinistra Anti Semi Join

SELECT * FROM A WHERE X NOT IN (SELECT Y FROM B);

X
----
Amy
John

ATTENZIONE: fai attenzione se stai usando NOT IN su una colonna NULABILE! Maggiori dettagli qui .


Giusto Anti Semi Join

Include le righe giuste che non corrispondono alle righe a sinistra.

Giusto Anti Semi Join

SELECT * FROM B WHERE Y NOT IN (SELECT X FROM A);

Y
-------
Tim
Vincent

Come puoi vedere, non esiste una sintassi NOT IN dedicata per l'anti semi join sinistro e destro - otteniamo l'effetto semplicemente cambiando le posizioni della tabella all'interno del testo SQL.


Cross Join

Un prodotto cartesiano di tutti lasciato con tutte le righe giuste.

SELECT * FROM A CROSS JOIN B;

X      Y
-----  -------
Amy    Lisa
John   Lisa
Lisa   Lisa
Marco  Lisa
Phil   Lisa
Amy    Marco
John   Marco
Lisa   Marco
Marco  Marco
Phil   Marco
Amy    Phil
John   Phil
Lisa   Phil
Marco  Phil
Phil   Phil
Amy    Tim
John   Tim
Lisa   Tim
Marco  Tim
Phil   Tim
Amy    Vincent
John   Vincent
Lisa   Vincent
Marco  Vincent
Phil   Vincent

Cross join equivale a un join interno con condizione join che corrisponde sempre, quindi la query seguente avrebbe restituito lo stesso risultato:

SELECT * FROM A JOIN B ON 1 = 1;

Self-Join

Questo semplicemente indica una tabella che si unisce a se stessa. Un self-join può essere uno dei tipi di join discussi sopra. Ad esempio, questo è un self-join interno:

SELECT * FROM A A1 JOIN A A2 ON LEN(A1.X) < LEN(A2.X);

X     X
----  -----
Amy   John
Amy   Lisa
Amy   Marco
John  Marco
Lisa  Marco
Phil  Marco
Amy   Phil


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow