Come posso SELEZIONARE le righe con MAX (valore colonna), DISTINCT di un'altra colonna in SQL?


768

Il mio tavolo è:

id  home  datetime     player   resource
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399 
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
3  | 10  | 03/03/2009 | john   | 300
4  | 11  | 03/03/2009 | juliet | 200
6  | 12  | 03/03/2009 | borat  | 500
7  | 13  | 24/12/2008 | borat  | 600
8  | 13  | 01/01/2009 | borat  | 700

Devo selezionare ogni distinto hometenendo il valore massimo di datetime.

Il risultato sarebbe:

id  home  datetime     player   resource 
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
8  | 13  | 01/01/2009 | borat  | 700

Ho provato:

-- 1 ..by the MySQL manual: 

SELECT DISTINCT
  home,
  id,
  datetime AS dt,
  player,
  resource
FROM topten t1
WHERE datetime = (SELECT
  MAX(t2.datetime)
FROM topten t2
GROUP BY home)
GROUP BY datetime
ORDER BY datetime DESC

Non funziona Il set di risultati ha 130 righe sebbene il database ne contenga 187. Il risultato include alcuni duplicati di home.

-- 2 ..join

SELECT
  s1.id,
  s1.home,
  s1.datetime,
  s1.player,
  s1.resource
FROM topten s1
JOIN (SELECT
  id,
  MAX(datetime) AS dt
FROM topten
GROUP BY id) AS s2
  ON s1.id = s2.id
ORDER BY datetime 

No. Fornisce tutti i record.

-- 3 ..something exotic: 

Con vari risultati.

Risposte:


940

Sei così vicino! Tutto quello che devi fare è selezionare ENTRAMBI la home e la sua data massima, quindi tornare alla toptentabella su ENTRAMBI i campi:

SELECT tt.*
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt 
ON tt.home = groupedtt.home 
AND tt.datetime = groupedtt.MaxDateTime

5
Provalo per distinto, se due uguali max datetime si trovano nella stessa casa (con giocatori diversi)
Maksym Gontar

5
Penso che il modo classico per farlo sia con un join naturale: "SELECT tt. * FROM topten tt NATURAL JOIN (SELECT home, MAX (datetime) AS datetime FROM topten GROUP BY home) mostrecent;" Stessa domanda esattamente, ma probabilmente più leggibile
Parker,

32
che dire se ci sono due righe che hanno gli stessi valori dei campi 'home' e 'datetime'?
Kemal Duran,

3
@Young il problema con la tua query è che potrebbe restituire id, playere resourcedi una riga non max per una determinata casa, ad esempio per home = 10, potresti ottenere: 3 | 10 | 04/03/2009 | john | 300 In altre parole, non garantisce che tutta la colonna di una riga nel set di risultati apparirà al massimo (datetime) per una determinata casa.
Sactiw,

1
@ me1111 problema con la tua query è che potrebbe / potrebbe non restituire la riga al massimo (datetime) per una determinata casa. Il motivo di essere GROUP BY prenderà qualsiasi riga casuale per ogni casa e ORDER BY
ordinerà

87

La MySQLsoluzione più veloce , senza domande interne e senza GROUP BY:

SELECT m.*                    -- get the row that contains the max value
FROM topten m                 -- "m" from "max"
    LEFT JOIN topten b        -- "b" from "bigger"
        ON m.home = b.home    -- match "max" row with "bigger" row by `home`
        AND m.datetime < b.datetime           -- want "bigger" than "max"
WHERE b.datetime IS NULL      -- keep only if there is no bigger than max

Spiegazione :

Unisciti alla tabella con se stessa usando la homecolonna. L'uso di LEFT JOINassicura che tutte le righe della tabella mvengano visualizzate nel set di risultati. Quelli che non hanno una corrispondenza nella tabella bavranno NULLs per le colonne di b.

L'altra condizione su JOINchiede di far corrispondere solo le righe bche hanno un valore maggiore sulla datetimecolonna rispetto alla riga da m.

Utilizzando i dati pubblicati nella domanda, LEFT JOINverranno prodotte queste coppie:

+------------------------------------------+--------------------------------+
|              the row from `m`            |    the matching row from `b`   |
|------------------------------------------|--------------------------------|
| id  home  datetime     player   resource | id    home   datetime      ... |
|----|-----|------------|--------|---------|------|------|------------|-----|
| 1  | 10  | 04/03/2009 | john   | 399     | NULL | NULL | NULL       | ... | *
| 2  | 11  | 04/03/2009 | juliet | 244     | NULL | NULL | NULL       | ... | *
| 5  | 12  | 04/03/2009 | borat  | 555     | NULL | NULL | NULL       | ... | *
| 3  | 10  | 03/03/2009 | john   | 300     | 1    | 10   | 04/03/2009 | ... |
| 4  | 11  | 03/03/2009 | juliet | 200     | 2    | 11   | 04/03/2009 | ... |
| 6  | 12  | 03/03/2009 | borat  | 500     | 5    | 12   | 04/03/2009 | ... |
| 7  | 13  | 24/12/2008 | borat  | 600     | 8    | 13   | 01/01/2009 | ... |
| 8  | 13  | 01/01/2009 | borat  | 700     | NULL | NULL | NULL       | ... | *
+------------------------------------------+--------------------------------+

Infine, la WHEREclausola mantiene solo le coppie che hanno NULLs nelle colonne di b(sono contrassegnate con *nella tabella sopra); ciò significa che, a causa della seconda condizione della JOINclausola, la riga selezionata mha il valore più grande nella colonna datetime.

Leggi gli SQL Antipatterns: evitare le insidie ​​del libro di programmazione del database per altri suggerimenti SQL.


Con SQLite, il primo è molto più lento della versione di La Voie quando non c'è indice sulla colonna corrispondente (cioè "home"). (Testato con 24k righe risultanti in 13k righe)
Thomas Tempelmann

10
Questa è la risposta migliore, se mostri il piano di esecuzione vedrai un passo in meno con questa query
TlmaK0

cosa succederà se 2 file hanno lo stesso homeed datetimee datetimeil massimo per quel particolare home?
Istiaque Ahmed,

Entrambe le righe vengono visualizzate nel set di risultati. Questa risposta è una prova di concetto. Nel tuo codice reale probabilmente hai un altro criterio per selezionarne solo uno in questa situazione (forse il primo o l'ultimo o usi un'altra colonna per decidere). Aggiungi questo criterio come una nuova condizione nella ONclausola. Fe ... ON ... AND m.id < b.idper mantenere la voce più recente (quella con il massimo id) quando due righe hanno gli stessi valori homee datetimecolonne ed è il massimo datetime.
axiac,

Quali indici sarebbe meglio ottimizzare per una query come questa?
AjaxLeung,

73

Ecco la versione T-SQL :

-- Test data
DECLARE @TestTable TABLE (id INT, home INT, date DATETIME, 
  player VARCHAR(20), resource INT)
INSERT INTO @TestTable
SELECT 1, 10, '2009-03-04', 'john', 399 UNION
SELECT 2, 11, '2009-03-04', 'juliet', 244 UNION
SELECT 5, 12, '2009-03-04', 'borat', 555 UNION
SELECT 3, 10, '2009-03-03', 'john', 300 UNION
SELECT 4, 11, '2009-03-03', 'juliet', 200 UNION
SELECT 6, 12, '2009-03-03', 'borat', 500 UNION
SELECT 7, 13, '2008-12-24', 'borat', 600 UNION
SELECT 8, 13, '2009-01-01', 'borat', 700

-- Answer
SELECT id, home, date, player, resource 
FROM (SELECT id, home, date, player, resource, 
    RANK() OVER (PARTITION BY home ORDER BY date DESC) N
    FROM @TestTable
)M WHERE N = 1

-- and if you really want only home with max date
SELECT T.id, T.home, T.date, T.player, T.resource 
    FROM @TestTable T
INNER JOIN 
(   SELECT TI.id, TI.home, TI.date, 
        RANK() OVER (PARTITION BY TI.home ORDER BY TI.date) N
    FROM @TestTable TI
    WHERE TI.date IN (SELECT MAX(TM.date) FROM @TestTable TM)
)TJ ON TJ.N = 1 AND T.id = TJ.id

EDIT
Sfortunatamente, non ci sono funzioni RANK () OVER in MySQL.
Ma può essere emulato, vedi Funzioni di emulazione analitica (ranking AKA) con MySQL .
Quindi questa è la versione di MySQL :

SELECT id, home, date, player, resource 
FROM TestTable AS t1 
WHERE 
    (SELECT COUNT(*) 
            FROM TestTable AS t2 
            WHERE t2.home = t1.home AND t2.date > t1.date
    ) = 0

scusa amico, # 1064 - Hai un errore nella sintassi SQL; controlla il manuale corrispondente alla versione del tuo server MySQL per la sintassi corretta da usare vicino a '() OVER (PARTITION BY krd ORDER BY daytime DESC) N FROM @rapsa) M WHERE N =' at line 1
Kaptah

2
ah, quindi stai usando MySQL. Ecco da dove dovresti iniziare! Aggiornerò presto la risposta.
Maksym Gontar,

@MaxGontar, la tua soluzione mysql è incredibile, grazie. cosa succede se nella tua @TestTable rimuovi la riga # 1>: SELECT 1, 10, '2009-03-04', 'john', 399, questo è, cosa succede se hai una singola riga per un dato valore di casa? grazie.
egidiocs,

2
ERRORE: Sostituisci "RANK ()" con "ROW_NUMBER ()". Se hai un pareggio (causato da un valore di data duplicato) avrai due record con "1" per N.
MikeTeeVee

29

Funzionerà anche se hai due o più righe per ognuna homecon uguali DATETIME:

SELECT id, home, datetime, player, resource
FROM   (
       SELECT (
              SELECT  id
              FROM    topten ti
              WHERE   ti.home = t1.home
              ORDER BY
                      ti.datetime DESC
              LIMIT 1
              ) lid
       FROM   (
              SELECT  DISTINCT home
              FROM    topten
              ) t1
       ) ro, topten t2
WHERE  t2.id = ro.lid

aggiunto il campo del coperchio nella tabella, No Good
Kaptah

1
Questo non è stato eseguito su PHPMyAdmin. La pagina si aggiorna ma non ci sono risultati né errori ..?
Kaptah,

WHERE ti.home = t1.home- puoi spiegare la sintassi?
Istiaque Ahmed,

@IstiaqueAhmed: che cosa esattamente non capisci qui? È una query correlata e l'espressione menzionata è una condizione di correlazione.
Quassnoi,

@Quassnoi, la selectquery che ha la riga WHERE ti.home = t1.home non necessita della FROMclausola che definisce t1. Quindi come viene usato?
Istiaque Ahmed,

26

Penso che questo ti darà il risultato desiderato:

SELECT   home, MAX(datetime)
FROM     my_table
GROUP BY home

MA se hai bisogno anche di altre colonne, crea un join con la tabella originale (controlla la Michael La Voierisposta)

I migliori saluti.


8
Ha bisogno anche di altre colonne.
Quassnoi,

4
id, home, datetime, player, resource
Quassnoi

17

Dal momento che le persone sembrano continuare a incontrare questo thread (la data dei commenti varia da 1,5 anni) non è molto più semplice:

SELECT * FROM (SELECT * FROM topten ORDER BY datetime DESC) tmp GROUP BY home

Non sono necessarie funzioni di aggregazione ...

Saluti.


6
Questo non sembra funzionare. Messaggio di errore: la colonna 'x' non è valida nell'elenco di selezione perché non è contenuta né in una funzione aggregata né nella clausola GROUP BY.
Polla il

Questo sicuramente non funzionerà in SQL Server o Oracle, anche se sembra che potrebbe funzionare in MySQL.
ErikE,

Questo è davvero bellissimo! Come funziona? Usando DESC e la colonna di ritorno del gruppo predefinito? Quindi se lo cambiassi in ASC datetime, restituirebbe la prima riga per ogni casa?
wayofthefuture,

È brillante!
Amante dei cani,

Questo straight-up non funziona se hai colonne non aggregate (in MySQL).
user3562927

11

Puoi anche provare questo e per le tabelle di grandi dimensioni le prestazioni delle query saranno migliori. Funziona quando non ci sono più di due record per ogni casa e le loro date sono diverse. Una query MySQL generale migliore è quella di Michael La Voie sopra.

SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
FROM   t_scores_1 t1 
INNER JOIN t_scores_1 t2
   ON t1.home = t2.home
WHERE t1.date > t2.date

O nel caso di Postgres o quei dbs che forniscono funzioni analitiche, prova

SELECT t.* FROM 
(SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
  , row_number() over (partition by t1.home order by t1.date desc) rw
 FROM   topten t1 
 INNER JOIN topten t2
   ON t1.home = t2.home
 WHERE t1.date > t2.date 
) t
WHERE t.rw = 1

Questa risposta è corretta? Ho provato a usarlo, ma sembra non selezionare il record con la data più recente per "home", ma rimuove solo il record con la data più vecchia. Ecco un esempio: SQLfiddle
marcin93w,

1
@kidOfDeath - Aggiornato la mia risposta con il contesto e la query Postgres
Shiva,

Con SQLite, il primo è molto più lento della versione di La Voie quando non c'è indice sulla colonna corrispondente (cioè "home").
Thomas Tempelmann,

8

Questo funziona su Oracle:

with table_max as(
  select id
       , home
       , datetime
       , player
       , resource
       , max(home) over (partition by home) maxhome
    from table  
)
select id
     , home
     , datetime
     , player
     , resource
  from table_max
 where home = maxhome

1
come viene scelto il massimo datetime? ha chiesto di raggruppare per casa e selezionare max datetime. Non vedo come questo fa.
n00b,

8
SELECT  tt.*
FROM    TestTable tt 
INNER JOIN 
        (
        SELECT  coord, MAX(datetime) AS MaxDateTime 
        FROM    rapsa 
        GROUP BY
                krd 
        ) groupedtt
ON      tt.coord = groupedtt.coord
        AND tt.datetime = groupedtt.MaxDateTime

8

Prova questo per SQL Server:

WITH cte AS (
   SELECT home, MAX(year) AS year FROM Table1 GROUP BY home
)
SELECT * FROM Table1 a INNER JOIN cte ON a.home = cte.home AND a.year = cte.year

5
SELECT c1, c2, c3, c4, c5 FROM table1 WHERE c3 = (select max(c3) from table)

SELECT * FROM table1 WHERE c3 = (select max(c3) from table1)

5

Ecco la versione di MySQL che stampa solo una voce in cui ci sono duplicati MAX (datetime) in un gruppo.

Puoi testare qui http://www.sqlfiddle.com/#!2/0a4ae/1

Dati di esempio

mysql> SELECT * from topten;
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    3 |   10 | 2009-03-03 00:00:00 | john   |      300 |
|    4 |   11 | 2009-03-03 00:00:00 | juliet |      200 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    6 |   12 | 2009-03-03 00:00:00 | borat  |      500 |
|    7 |   13 | 2008-12-24 00:00:00 | borat  |      600 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+

Versione MySQL con variabile utente

SELECT *
FROM (
    SELECT ord.*,
        IF (@prev_home = ord.home, 0, 1) AS is_first_appear,
        @prev_home := ord.home
    FROM (
        SELECT t1.id, t1.home, t1.player, t1.resource
        FROM topten t1
        INNER JOIN (
            SELECT home, MAX(datetime) AS mx_dt
            FROM topten
            GROUP BY home
          ) x ON t1.home = x.home AND t1.datetime = x.mx_dt
        ORDER BY home
    ) ord, (SELECT @prev_home := 0, @seq := 0) init
) y
WHERE is_first_appear = 1;
+------+------+--------+----------+-----------------+------------------------+
| id   | home | player | resource | is_first_appear | @prev_home := ord.home |
+------+------+--------+----------+-----------------+------------------------+
|    9 |   10 | borat  |      700 |               1 |                     10 |
|   10 |   11 | borat  |      700 |               1 |                     11 |
|   12 |   12 | borat  |      700 |               1 |                     12 |
|    8 |   13 | borat  |      700 |               1 |                     13 |
+------+------+--------+----------+-----------------+------------------------+
4 rows in set (0.00 sec)

Ritaglio delle risposte accettate

SELECT tt.*
FROM topten tt
INNER JOIN
    (
    SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home
) groupedtt ON tt.home = groupedtt.home AND tt.datetime = groupedtt.MaxDateTime
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+
7 rows in set (0.00 sec)

Adoro anche questa risposta, dato che mi sta aiutando così tanto, devo indicare un grosso difetto, che dipende dal sistema mysql usato. Fondamentalmente, questa soluzione si basa sulla clausola ORDER BY in subselect. Questo potrebbe, o potrebbe non funzionare in vari ambienti mysql. Non l'ho provato su MySQL puro, ma sicuramente non funziona AFFIDABILMENTE su MariaDB 10.1, come spiegato qui stackoverflow.com/questions/26372511/…, ma lo stesso codice funziona bene su Percona Server. Per essere precisi, POTREBBE o POTREBBE NON ottenere gli stessi risultati, a seconda della quantità di colonne t1.
Radek,

L'esempio per questa affermazione è che su MariaDB 10.1 ha funzionato, quando ho usato 5 colonne dalla tabella t1. Non appena ho aggiunto la sesta colonna, ovviamente scherzando con l'ordinamento dei dati "naturale" nella tabella originale, ha smesso di funzionare. Il motivo è che i dati in subselect sono diventati non ordinati e quindi ho avuto la condizione "is_first_appear = 1" soddisfatta più volte. Lo stesso codice, con gli stessi dati, ha funzionato su Percona ok.
Radek,

5

Un altro modo per ottenere la riga più recente per gruppo utilizzando una query secondaria che in sostanza calcola un ranking per ogni riga per gruppo e quindi filtra le righe più recenti come con rank = 1

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and a.`datetime` < b.`datetime`
) +1 = 1

DEMO

Ecco la demo visiva per il grado no per ogni riga per una migliore comprensione

Leggendo alcuni commenti, che dire se ci sono due righe che hanno gli stessi valori di campo "home" e "datetime"?

La query precedente non riuscirà e restituirà più di 1 riga per la situazione precedente. Per coprire questa situazione ci sarà bisogno di un altro criterio / parametro / colonna per decidere quale riga dovrebbe essere presa quale rientra nella situazione sopra. Osservando il set di dati di esempio presumo che esista una colonna chiave primaria idche dovrebbe essere impostata su incremento automatico. Quindi possiamo usare questa colonna per selezionare la riga più recente modificando la stessa query con l'aiuto di CASEstatement like

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and  case 
       when a.`datetime` = b.`datetime`
       then a.id < b.id
       else a.`datetime` < b.`datetime`
       end
) + 1 = 1

DEMO

La query sopra selezionerà la riga con l'id più alto tra gli stessi datetimevalori

demo visiva per il grado no per ogni riga


2

Perché non usare: SELEZIONA home, MAX (datetime) AS MaxDateTime, player, risorsa DA anche a GRUPPO BY home Mi sono perso qualcosa?


4
Sarebbe valido solo con MySQL, e solo le versioni precedenti alla 5.7 (?) O dopo la 5.7 con ONLY_FULL_GROUP_BY disabilitate, poiché SELEZIONA colonne non aggregate / Raggruppate (player, risorsa), il che significa che MySQL fornirà valori scelti casualmente per quelle due campi risultato. Non sarebbe un problema per la colonna del giocatore poiché è correlata alla colonna principale, ma la colonna delle risorse non sarebbe correlata con la colonna principale o datetime e non potresti garantire quale valore di risorsa riceveresti.
simpleuser,

+1 per la spiegazione, MA ha scritto la domanda posta questa query non restituirà l' expectedoutput in MySQL versione 5.6 e beforedubito fortemente che si comporti diversamente in MySQL versione 5.7 e after.
Sactiw,

@simpleuser, `Non sarebbe un problema per la colonna del giocatore in quanto correlata alla colonna principale` - puoi spiegarci di più?
Istiaque Ahmed,

@IstiaqueAhmed mentre la guardo di nuovo, questa affermazione non è corretta. Avevo pensato che ogni giocatore avesse sempre lo stesso valore di casa, ma ora vedo che non lo sono, quindi lo stesso problema di selezione casuale si verificherà anche per quella colonna
simpleuser

1

Prova questo

select * from mytable a join
(select home, max(datetime) datetime
from mytable
group by home) b
 on a.home = b.home and a.datetime = b.datetime

Saluti K


5
Provalo per distinto, se due uguali max datetime si trovano nella stessa casa (con giocatori diversi)
Maksym Gontar

l'alias per max(datetime) è datetime. Non farà alcun problema?
Istiaque Ahmed,

Come viene datetimeselezionato il più alto ?
Istiaque Ahmed,

1

questa è la domanda che ti serve:

 SELECT b.id, a.home,b.[datetime],b.player,a.resource FROM
 (SELECT home,MAX(resource) AS resource FROM tbl_1 GROUP BY home) AS a

 LEFT JOIN

 (SELECT id,home,[datetime],player,resource FROM tbl_1) AS b
 ON  a.resource = b.resource WHERE a.home =b.home;

puoi spiegare la tua risposta?
Istiaque Ahmed,

1

@Michae La risposta accettata funzionerà bene nella maggior parte dei casi, ma fallisce per uno come sotto.

Nel caso in cui ci fossero 2 righe con HomeID e Datetime uguali, la query restituirà entrambe le righe, non HomeID distinto come richiesto, per cui aggiungere Distinct nella query come di seguito.

SELECT DISTINCT tt.home  , tt.MaxDateTime
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt 
ON tt.home = groupedtt.home 
AND tt.datetime = groupedtt.MaxDateTime

risultato mostra - "# 1054 - Colonna sconosciuta 'tt.MaxDateTime' nella 'lista dei campi'"
Istiaque Ahmed

@IstiaqueAhmed hai archiviato MaxDatetime cioè un nome di colonna come quello ..?
Manoj Kargeti,

No, la tabella in OP non ha alcuna colonna del genere.
Istiaque Ahmed,

l'errore dice anche lo stesso per favore ... cosa vuoi fare esattamente? puoi inviare la struttura della tabella e la tua query.
Manoj Kargeti,

1

Spero sotto query fornirà l'output desiderato:

Select id, home,datetime,player,resource, row_number() over (Partition by home ORDER by datetime desc) as rownum from tablename where rownum=1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.