Salve,
la 2* richiesta e' tendenzialmente facile, basta utilizzare un raggruppamento con filtro sulla cardinalita' dello stesso, quindi, trivialmente,
... TABLE dbo.t (
Id int NOT NULL
);
GO
INSERT INTO dbo.t
VALUES ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 )
, ( 3 ), ( 4 ); -- <- doppioni
GO
SELECT t.[Id]
FROM dbo.t t
GROUP BY t.[Id]
HAVING COUNT(*) > 1
ORDER BY t.[Id];
--<-----------
Id
-----------
3
4
la prima delle 2 domande ha invece soluzioni "complicate" dal punto di vista computazionale...
ragionando in termini di insiemi, dobbiamo confrontare il set "reale" con un set dinamico e ricavarne gli eventuali gap...
spesso, nei database, esistono tabelle apposite di appoggio, tabelle costituite dalla serie dei numeri interi da 0 a n (dove "n" e' un numero bello grande) al fine proprio di risolvere questa tipologia di problematiche...
in assenza di queste, possiamo produrre ad esempio tramite Common Table Epression ricorsiva un set in questo senso, da utilizzare poi in JOIN per l'individuazione dei gap eventualmente presenti.
possiamo quindi ad esempio basarci sul bellissimo spunto di Itzik Ben Gan, MVP di SQL Server e geniale mente matematica, che utilizzando una serie di Common Table Expressions innestate con operazioni di cross join, genera "al volo" il nostro SET composto dalla serie numerica finita e completa...
utilizzando piu' CTE si puo' incrementare la cardinalita' di tale proiezione di appoggio, ma oltre la presente esemplificazione si deve cambiare dominio e passare al tipo di dato bigint...
ricordo che nel caso la cardinalita' della tabella di origine da confrontare base sia gia' alta, questa operazione e' dispendiosa e, nel caso, conviene non operare "al volo" ma creare effettivamente la tabella numerica di appoggio gia' popolata definitivamente sul database o su altro database di appoggio..
il popolamento del set ausiliario e' ovviamente pesante se va eseguito spesso, ed in questo caso sarebbe bene disporre della tabella numerica ausiliaria, ma se e' un'operazione "una tantum", si puo' anche fare:
torniamo quindi all'implementazione, e sempre trivialmente continuiamo dal precedente, e popoliamo la tabella originale da 1 a 10k
PRINT 'caricamento iniziale';
DECLARE @i int = 1;
WHILE @i <= 10000 BEGIN
INSERT INTO dbo.[t] VALUES ( @i );
SET @i += 1;
END;
GO
e qui provvediamo prima alla generazione del set di serie progressiva numerica da 1 a n dove n == al "max" della tabella da "confrontare", e quindi proiettiamo il LEFT JOIN del risultato con la tabella di "origine",
GO
DECLARE @min int;
SELECT @min = MAX(t.[Id])
FROM [dbo].[t] t
SET @min = ISNULL(@min, 1);
SET @min += 0;
-- ottiene tabella di numeri
WITH Nbrs_4( n ) AS (
SELECT 1 UNION SELECT 0 ),
Nbrs_3( n ) AS (
SELECT 1 FROM Nbrs_4 n1 CROSS JOIN Nbrs_4 n2 ),
Nbrs_2( n ) AS (
SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2 ),
Nbrs_1( n ) AS (
SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2 ),
Nbrs_0( n ) AS (
SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2 ),
Nbrs ( n ) AS (
SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2 ),
-- scarta i numeri eccessivi
cUsed AS (
SELECT n
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY n)
FROM Nbrs
) D ( n )
WHERE n <= @min
)
-- avendo il set seriale completo da 1 a n, dove n == MAX(t.[Id]), possiamo evidenziare gli eventuali gap
SELECT c.[n] AS [Numero Assente]
FROM cUsed c
LEFT JOIN [dbo].[t] t ON t.[Id] = c.[n]
WHERE t.[Id] IS NULL
ORDER BY c.[n];
se eseguiamo "ora" il codice, avremo come risultato un empty set in quanto la serie della tabella di origine e' completa, quindi procediamo con
DELETE [dbo].[t]
WHERE ([Id] % 255 = 0);
al fine di cancellare 1 riga ogni 255 (togliamo 39 righe), e rieseguendo la query di cui sopra, troveremo tutti i gap risultanti.
salutoni romagnoli
--
Andrea