Ripristina seed identità dopo aver eliminato i record in SQL Server


682

Ho inserito i record in una tabella del database di SQL Server. La tabella aveva una chiave primaria definita e il seed dell'identità di incremento automatico è impostato su "Sì". Questo viene fatto principalmente perché in SQL Azure, ogni tabella deve avere una chiave primaria e un'identità definite.

Ma dal momento che devo eliminare alcuni record dalla tabella, il seme identità per quelle tabelle sarà disturbato e la colonna dell'indice (che viene generata automaticamente con un incremento di 1) verrà disturbata.

Come posso ripristinare la colonna identità dopo aver eliminato i record in modo che la colonna abbia una sequenza in ordine numerico crescente?

La colonna Identity non viene utilizzata come chiave esterna in nessun punto del database.


4
"in SQL Azure" - "ogni tabella deve avere una chiave primaria" - true - "e Identity Defined" - false. Identità e chiave primaria sono concetti ortogonali. Una colonna di identità non deve essere il PK di una tabella. Una chiave primaria non deve essere una colonna di identità.
Damien_The_Unbeliever,

OK. Il mio concetto potrebbe essere sbagliato. Ma ora ho definito la struttura della tabella con PK e Identity Seed. Se devo eliminare alcune righe, come posso ripristinare Identity Seed in un ordine crescente numerico corretto
xorpower,

29
Direi sempre che se ti preoccupi degli effettivi valori numerici generati in una colonna di identità, li stai abusando. Tutto ciò di cui dovresti preoccuparti con una colonna di identità è che genera automaticamente valori univoci (yay!) E che puoi memorizzare questi valori in una colonna numerica (questo bit è rilevante solo per dichiarare che le colonne contengono questi valori). Non dovresti mostrarli a nessuno, quindi non dovrebbe importare quali valori assumono.
Damien_The_Unbeliever,

puoi usare dbcc check identify come altri menzionati ma tieni presente che la chiave primaria non è obbligatoria per sql db v12
Satya_MSFT

Risposte:


1100

Il DBCC CHECKIDENTcomando di gestione viene utilizzato per ripristinare il contatore di identità. La sintassi del comando è:

DBCC CHECKIDENT (table_name [, { NORESEED | { RESEED [, new_reseed_value ]}}])
[ WITH NO_INFOMSGS ]

Esempio:

DBCC CHECKIDENT ('[TestTable]', RESEED, 0);
GO

Non era supportato in una versione precedente del database SQL di Azure, ma ora è supportato.


Si noti che l' new_reseed_valueargomento è variato tra le versioni di SQL Server in base alla documentazione :

Se le righe sono presenti nella tabella, la riga successiva viene inserita con il valore new_reseed_value . Nella versione SQL Server 2008 R2 e precedenti, la riga successiva inserita utilizza new_reseed_value + il valore di incremento corrente.

Tuttavia, trovo queste informazioni fuorvianti (in realtà è semplicemente sbagliato) perché il comportamento osservato indica che almeno SQL Server 2012 utilizza ancora new_reseed_value + la logica del valore di incremento corrente. Microsoft è persino in contraddizione con la propria Example Ctrovata sulla stessa pagina:

C. Forzare il valore di identità corrente su un nuovo valore

L'esempio seguente forza il valore di identità corrente nella colonna AddressTypeID nella tabella AddressType su un valore di 10. Poiché la tabella ha righe esistenti, la riga successiva inserita utilizzerà 11 come valore, ovvero il nuovo valore di incremento corrente definito per il valore della colonna più 1.

USE AdventureWorks2012;  
GO  
DBCC CHECKIDENT ('Person.AddressType', RESEED, 10);  
GO

Tuttavia, tutto ciò lascia un'opzione per comportamenti diversi nelle versioni più recenti di SQL Server. Immagino che l'unico modo per essere sicuri, fino a quando Microsoft non chiarirà le cose nella sua stessa documentazione, sia quello di eseguire test effettivi prima dell'uso.


24
La sintassi sarebbe ... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
Biki

2
Sembra che DBCC CHECKIDENTsia supportato a partire dalla prossima versione (V12 / Sterling): azure.microsoft.com/en-us/documentation/articles/… Anche se, per questa situazione particolare, consiglierei comunque TAVOLA DI
TRUNCATO

1
Non ha funzionato per me fino a quando "GO" non è stato su un'altra riga.
mrówa,

1
La sintassi viene contrassegnata a causa della parola chiave GO nella stessa riga, non so perché. Puoi spostarlo lungo una linea. Ho copiato e incollato questa riga 50 volte e ora devo tornare indietro e ripararlo.
AnotherDeveloper

4
Ha funzionato perfettamente per me. Vale la pena sottolineare che quando si esegue nuovamente il seeding di una tabella, se si desidera eseguire il reseing in modo che il primo record sia l'ID 1, il comando resed deve riportare a 0, in modo che il record successivo sia l'ID 1.
Mike Upjohn

216
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

Dove 0 è il identityvalore iniziale


15
Se la tabella è vuota, ad esempio se hai appena chiamato TRUNCATE, il nuovo valore seed dovrebbe essere il valore da utilizzare successivamente (ovvero 1 non 0). Se la tabella non è vuota, utilizzerà il new_reseed_value + 1. MSDN
kjbartel,

2
@kjbartel, Anil e altri: non è semplice come "se la tabella è vuota". La documentazione mancava il caso di quando la tabella è vuota a causa DELETE, non TRUNCATE, nel qual caso lo è anche new_reseed+value + 1. Ho scritto un post su questo, mostrando il comportamento effettivo tramite alcuni test e aggiornato il documento effettivo (ora che possiamo grazie al fatto che è su GitHub): Come funziona davvero DBCC CHECKIDENT quando ripristina il seme di identità (RESEED)? .
Solomon Rutzky,

87

Va notato che SE tutti i dati vengono rimossi dalla tabella tramite la DELETE(ovvero nessuna WHEREclausola), quindi fino a quando le autorizzazioni lo consentono, e b) non ci sono FK che fanno riferimento alla tabella (che sembra essere il caso qui), TRUNCATE TABLEsarebbe preferibile usare come fa un più efficiente DELETE e reimposta il IDENTITYseme allo stesso tempo. I seguenti dettagli sono presi dalla pagina MSDN per TRUNCATE TABLE :

Rispetto all'istruzione DELETE, TRUNCATE TABLE presenta i seguenti vantaggi:

  • Viene utilizzato meno spazio per il registro delle transazioni.

    L'istruzione DELETE rimuove le righe una alla volta e registra una voce nel registro delle transazioni per ogni riga eliminata. TRUNCATE TABLE rimuove i dati deallocando le pagine di dati utilizzate per archiviare i dati della tabella e registra solo le deallocazioni di pagine nel registro delle transazioni.

  • Vengono generalmente utilizzati meno blocchi.

    Quando l'istruzione DELETE viene eseguita utilizzando un blocco di riga, ogni riga nella tabella viene bloccata per l'eliminazione. TRUNCATE TABLE blocca sempre la tabella (incluso un blocco dello schema (SCH-M)) e la pagina ma non ogni riga.

  • Senza eccezioni, nella tabella rimangono zero pagine.

    Dopo l'esecuzione di un'istruzione DELETE, la tabella può ancora contenere pagine vuote. Ad esempio, le pagine vuote in un heap non possono essere deallocate senza almeno un blocco tabella esclusivo (LCK_M_X). Se l'operazione di eliminazione non utilizza un blocco tabella, la tabella (heap) conterrà molte pagine vuote. Per gli indici, l'operazione di eliminazione può lasciare indietro pagine vuote, sebbene queste pagine vengano rapidamente dislocate da un processo di pulizia in background.

Se la tabella contiene una colonna identità, il contatore per quella colonna viene reimpostato sul valore seed definito per la colonna. Se non è stato definito alcun seme, viene utilizzato il valore predefinito 1. Per conservare il contatore di identità, utilizzare invece CANC.

Quindi il seguente:

DELETE FROM [MyTable];
DBCC CHECKIDENT ('[MyTable]', RESEED, 0);

Diventa solo:

TRUNCATE TABLE [MyTable];

Si prega di consultare la TRUNCATE TABLEdocumentazione (collegata sopra) per ulteriori informazioni sulle restrizioni, ecc.


8
Sebbene sia più efficiente nelle circostanze corrette, questa non è sempre un'opzione. Il troncamento non verrà eseguito su una tabella con un FK definito rispetto ad esso. Anche quando non ci sono record dipendenti, il troncamento fallirà se il vincolo esiste. Anche il troncamento richiede autorizzazioni ALTER in cui Elimina richiede solo ELIMINA.
Rozwel,

3
@Rozwel Vero, ma avevo già qualificato la mia risposta affermando che le autorizzazioni appropriate devono essere in atto. Inoltre, la domanda specifica che non ci sono FK. Tuttavia, per motivi di chiarezza, ho aggiornato per specificare la restrizione "no FK". Grazie per la segnalazione.
Solomon Rutzky,

1
l'unico cavillo è che qualsiasi FK bloccherà il troncamento. È possibile (sebbene insolito) avere un FK rispetto a un vincolo univoco che non fa parte delle colonne PK o identità.
Rozwel,

1
@Rozwel Ancora una volta vero, ma sembra ragionevole supporre dalla domanda che non ci siano vincoli univoci dato che il PK esiste solo a causa della comprensione dell'OP (corretta o no) che è richiesto dal database SQL di Azure. Indipendentemente da ciò, sono tutto per ridurre l'ambiguità, quindi ho aggiornato di nuovo. Grazie.
Solomon Rutzky,

Non è poi così insolito avere una chiave esterna su un tavolo e la presenza di QUALSIASI chiave esterna vieta TRUNCATE TABLE. L'ho scoperto oggi nel modo più difficile oggi quando ho provato a eseguire TRUNCATE TABLE su una tabella che ha una chiave esterna che viene applicata contro altre due colonne nella tabella e un indice univoco nella tabella esterna.
David A. Gray,

83

Sebbene la maggior parte delle risposte stia suggerendo RESEED a 0, ma molte volte è necessario eseguire il resed al prossimo ID disponibile

declare @max int
select @max=max([Id])from [TestTable]
if @max IS NULL   //check when max is returned as null
  SET @max = 0
DBCC CHECKIDENT ('[TestTable]', RESEED,@max)

Questo controllerà la tabella e ripristinerà l'ID successivo.


2
Questa è l'unica risposta che funziona il 100% delle volte
Reversed Engineer

3
Un po 'più breve:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
Guillermo Prandi

61

Ho provato a @anil shahsrispondere e ha ripristinato l'identità. Ma quando è stata inserita una nuova riga ha ottenuto il identity = 2. Quindi invece ho modificato la sintassi in:

DELETE FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED, 0)
GO

Quindi la prima riga otterrà l'identità = 1.



16

Anche se la maggior parte delle risposte sono suggerendo RESEEDa 0, e mentre alcuni vedono questo come un difetto per TRUNCATEDle tabelle, Microsoft ha una soluzione che esclude laID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Questo verificherà la tabella e ripristinerà alla successiva ID. Questo è disponibile da MS SQL 2005 ad oggi.

https://msdn.microsoft.com/en-us/library/ms176057.aspx


1
Sfortunatamente non è vero. L'ho appena verificato per il server MS SQL 2014.
alehro,

1
In realtà, è vero per SQL 2014. L'ho appena testato e ha funzionato per me.
Daniel Dyson,

2
Questo funziona in modo incoerente per me su SQL 2012. A volte utilizza il prossimo disponibile come mi sarei aspettato, a volte sembra bloccato su un vecchio valore dalla tabella. Specifica delle opere alwasy di seme.
Dan Field,

Non funziona per me su SQL 2016: lascia il seme di identità così com'è. Potrebbe aver funzionato correttamente per me una volta, ma potrebbe anche essere stato il mio problema con le dita. Non riesco a farlo funzionare di nuovo
Reversed Engineer

Il messaggio indica il successo, Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.ma su nuovi inserti utilizza ancora il seme errato.
Denziloe,

7

l'emissione del comando 2 può fare il trucco

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

il primo reimposta l'identità su zero e il successivo la imposterà sul prossimo valore disponibile - jacob


2
DBCC CHECKIDENT ('[TestTable]', RESEED) non esegue il reseeding al prossimo valore disponibile
Atal Kishore

Questo è il metodo utilizzato da RedGate Data Compare quando l'opzione "Reseed Identity Columns" è attivata. L'ho provato ampiamente (intendo nel codice SQL, non nello strumento RedGate) e funziona in modo affidabile. (Non ho alcuna relazione con RedGate se non essere un utente occasionale delle loro versioni di prova)
Ingegnere invertito

6

@Giacobbe

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

Ha funzionato per me, ho solo dovuto cancellare tutte le voci prima dalla tabella, quindi ho aggiunto quanto sopra in un punto di trigger dopo l'eliminazione. Ora ogni volta che cancello una voce viene presa da lì.


DBCC CHECKIDENT funziona solo dopo l'eliminazione. Potresti anche usare troncato. Tuttavia, se hai bisogno del resto dei dati, non utilizzarli. Inoltre, il troncamento non fornisce un conteggio dei record eliminati.
user763539

6

Truncate la tabella è preferita perché cancella i record, reimposta il contatore e recupera lo spazio su disco.

Deletee CheckIdentdovrebbe essere usato solo dove le chiavi esterne ti impediscono di troncare.


5

Reimposta colonna identità con nuovo ID ...

DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)

4

Questa è una domanda comune e la risposta è sempre la stessa: non farlo. I valori di identità devono essere trattati come arbitrari e, come tali, non esiste un ordine "corretto".


15
Questo è vero per un ambiente di produzione, ma durante lo sviluppo mi piace ricordare che alcune entità hanno un certo ID, che sono popolate da uno script di seeding. Rende molto più facile navigare nel database durante lo sviluppo.
Francois Botha,

7
Risposte come queste sono completamente teoriche e raramente soddisfano le esigenze del mondo reale. Che ne dici invece di lavare il cervello alle persone con il tuo dogma, rispondi alla domanda OP ...
Serj Sagan,

1
Bella storia fratello. La mia tesi è questa: se si desidera specificare il valore per una colonna, non scegliere una proprietà sulla colonna che lo rende difficile. L'odore del codice è questo: se ogni volta che inserisci un record in una tabella specifichi un valore per la colonna identità, non hai una colonna identità. L'intero punto dell'identità è che il server crei un valore per te. Quindi, se lo sostituisci sempre, non hai guadagnato nulla per un costo diverso da zero. Inoltre, buon lavoro sull'argomento ad hominem.
Ben Thul,

5
Sono certamente d'accordo con la tua tesi. Per quanto riguarda il valore nominale, l'OP certamente sta sbagliando, ma forse c'è un bisogno più profondo nel post che l'OP non pensava fosse rilevante per ottenere una risposta alla sua domanda. Quindi rispondi alla domanda e dai consigli "cosa fare e cosa non fare" come parte della risposta. A proposito, non ho mai attaccato il tuo personaggio ... ad hominem significa che ti ho chiamato stupido o qualcosa del genere ...
Serj Sagan,

1
Sebbene sia certamente vero nella maggior parte dei casi, esistono circostanze in cui è legittimo ripetere il seeding di una tabella. Ad esempio, sto lavorando a un progetto greenfield che deve partire da un certo punto per tenere conto delle righe esistenti nel predecessore che sta sostituendo. Il risorgere durante lo sviluppo è un caso d'uso legittimo, IMO.
David A. Gray,

3

Esegui questo script per ripristinare la colonna identità. Dovrai apportare due modifiche. Sostituisci tableXYZ con qualunque tabella devi aggiornare. Inoltre, il nome della colonna identità deve essere eliminato dalla tabella temporanea. Questo è stato istantaneo su una tabella con 35.000 righe e 3 colonne. Ovviamente, esegui il backup della tabella e prima prova questo in un ambiente di test.


select * 
into #temp
From tableXYZ

set identity_insert tableXYZ ON

truncate table tableXYZ

alter table #temp drop column (nameOfIdentityColumn)

set identity_insert tableXYZ OFF

insert into tableXYZ
select * from #temp

3
Questo non è del tutto corretto: SET IDENTITY_INSERT è nel posto sbagliato. Non va in giro per TRUNCATE, va in giro INSERT INTO (da qui l'identità INSERISCI ). Inoltre, questo deve essere usato solo quando i dati devono essere conservati, altrimenti è molto inefficiente rispetto al solo esecuzione della singola istruzione TRUNCATE.
Solomon Rutzky,

1
DBCC CHECKIDENT (<TableName>, reseed, 0)

Ciò imposterà il valore dell'identità corrente su 0.

Inserendo il valore successivo, il valore dell'identità viene incrementato a 1.


1

Utilizzare questa procedura memorizzata:

IF (object_id('[dbo].[pResetIdentityField]') IS NULL)
  BEGIN
    EXEC('CREATE PROCEDURE [dbo].[pResetIdentityField] AS SELECT 1 FROM DUMMY');
  END
GO

SET  ANSI_NULLS ON
GO
SET  QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[pResetIdentityField]
  @pSchemaName NVARCHAR(1000)
, @pTableName NVARCHAR(1000) AS
DECLARE @max   INT;
DECLARE @fullTableName   NVARCHAR(2000) = @pSchemaName + '.' + @pTableName;

DECLARE @identityColumn   NVARCHAR(1000);

SELECT @identityColumn = c.[name]
FROM sys.tables t
     INNER JOIN sys.schemas s ON t.[schema_id] = s.[schema_id]
     INNER JOIN sys.columns c ON c.[object_id] = t.[object_id]
WHERE     c.is_identity = 1
      AND t.name = @pTableName
      AND s.[name] = @pSchemaName

IF @identityColumn IS NULL
  BEGIN
    RAISERROR(
      'One of the following is true: 1. the table you specified doesn''t have an identity field, 2. you specified an invalid schema, 3. you specified an invalid table'
    , 16
    , 1);
    RETURN;
  END;

DECLARE @sqlString   NVARCHAR(MAX) = N'SELECT @maxOut = max(' + @identityColumn + ') FROM ' + @fullTableName;

EXECUTE sp_executesql @stmt = @sqlString, @params = N'@maxOut int OUTPUT', @maxOut = @max OUTPUT

IF @max IS NULL
  SET @max = 0

print(@max)

DBCC CHECKIDENT (@fullTableName, RESEED, @max)
go

--exec pResetIdentityField 'dbo', 'Table'

Sto solo rivisitando la mia risposta. Mi sono imbattuto in un comportamento strano in SQL Server 2008 R2 di cui dovresti essere a conoscenza.

drop table test01

create table test01 (Id int identity(1,1), descr nvarchar(10))

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

delete from test01

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

La prima selezione produce 0, Item 1.

Il secondo produce 1, Item 1. Se si esegue il ripristino subito dopo la creazione della tabella, il valore successivo è 0. Onestamente, non mi sorprende che Microsoft non riesca a ottenere correttamente queste cose. L'ho scoperto perché ho un file di script che popola le tabelle di riferimento che a volte eseguo dopo aver ricreato le tabelle e talvolta quando le tabelle sono già state create.


1

Per fare questo uso il seguente script. C'è solo uno scenario in cui produrrà un "errore", ovvero se hai eliminato tutte le righe dalla tabella, ed IDENT_CURRENTè attualmente impostato su 1, ovvero c'era solo una riga nella tabella per cominciare.

DECLARE @maxID int = (SELECT MAX(ID) FROM dbo.Tbl)
;

IF @maxID IS NULL
    IF (SELECT IDENT_CURRENT('dbo.Tbl')) > 1
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 0)
    ELSE
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 1)
    ;
ELSE
    DBCC CHECKIDENT ('dbo.Tbl', RESEED, @maxID)
;

0

Per una completa ELIMINA righe e reimpostare il conteggio IDENTITÀ, utilizzo questo (SQL Server 2008 R2)

USE mydb

-- ##################################################################################################################
-- DANGEROUS!!!! USE WITH CARE
-- ##################################################################################################################

DECLARE
  db_cursor CURSOR FOR
    SELECT TABLE_NAME
      FROM INFORMATION_SCHEMA.TABLES
     WHERE TABLE_TYPE = 'BASE TABLE'
       AND TABLE_CATALOG = 'mydb'

DECLARE @tblname VARCHAR(50)
SET @tblname = ''

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @tblname

WHILE @@FETCH_STATUS = 0
BEGIN
  IF CHARINDEX('mycommonwordforalltablesIwanttodothisto', @tblname) > 0
    BEGIN
      EXEC('DELETE FROM ' + @tblname)
      DBCC CHECKIDENT (@tblname, RESEED, 0)
    END

  FETCH NEXT FROM db_cursor INTO @tblname
END

CLOSE db_cursor
DEALLOCATE db_cursor
GO

0

Reseeding a 0 non è molto pratico a meno che non si stia ripulendo il tavolo nel suo insieme.

altro saggio la risposta data da Anthony Raymond è perfetta. Ottieni prima il massimo della colonna identità, quindi esegui il seeding con max.


0

Ho provato a farlo per un gran numero di tavoli durante lo sviluppo, e questo funziona come un incantesimo.

DBCC CHECKIDENT('www.newsType', RESEED, 1);
DBCC CHECKIDENT('www.newsType', RESEED);

Quindi, devi prima impostarlo su 1, quindi impostarlo sull'indice più alto delle righe presenti nella tabella. Riposo facile e veloce dell'idex.


-2

È sempre meglio usare TRUNCATE quando possibile invece di eliminare tutti i record in quanto non utilizza anche lo spazio di registro.

Nel caso in cui sia necessario eliminare e ripristinare il seed, tenere sempre presente che se la tabella non è mai stata popolata e si è utilizzato, il DBCC CHECKIDENT('tablenem',RESEED,0) primo record otterrà identità = 0 come indicato nella documentazione di msdn

Nel tuo caso ricostruisci solo l'indice e non preoccuparti di perdere la serie di identità in quanto questo è uno scenario comune.


3
Mi sembra che l'idea sia quella di eliminare solo alcuni record.
Drumbeg,

6
Questo è semplicemente sbagliato - Non è <i> SEMPRE </i> meglio usare troncato e, in effetti, è solo meglio in alcuni scenari molto limitati e specifici. Il cielo proibisce a qualcuno di seguire il tuo consiglio e quindi di tornare indietro.
Thronk,

1
@Thronk Perché stai insinuando che TRUNCATEimpedirebbe ROLLBACKdi comportarsi come previsto? ROLLBACK esegue ancora il rollback. Anche se il DB è impostato su BULK_LOGGED.
Solomon Rutzky,

2
TRUNCATE è un'operazione DDL e non è registrato nel file di registro. A meno che non faccia parte della transazione (non menzionata in nessun punto della domanda o in questa risposta). Ogni volta che qualcuno dice che qualcosa è SEMPRE vero, è una scommessa abbastanza sicura che si sbagliano.
Thronk,

Questa è l' unica risposta che nota che c'è una differenza nel comportamento RESEED a seconda che la sequenza sia stata precedentemente utilizzata o meno. Un ridimensionamento dello stesso valore su più tabelle vuote , in cui alcune tabelle erano precedentemente popolate, comporterà valori iniziali diversi per il primo record inserito in ciascuna tabella.
Simon Coleman,

-4

Primo: specifica dell'identità Solo: "No" >> Salva Database Esegui progetto

Successivamente: Specifica dell'identità Solo: "SÌ" >> Salva database Esegui progetto

Il tuo ID database, PK Inizia da 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.