Cancellare item in una lista SharePoint con PowerShell
Normalmente per cancellare tutti gli item di una lista SharePoint si usa uno script PowerShell come questo:
Funziona bene con liste sotto i 5000 elementi, ma appena la lista cresce, diventa molto lento. Mi è capitato di dover cancellare la lista nascosta Workflow History che era arrivata a oltre 500000 items e si parlava di minuti per cancellare il singolo item, improponibile.
La lentezza è data dell'enumerazione $l.items che carica in memoria tutti gli items. Un altro effetto collaterale era il blocco di SharePoint durante la cancellazione.
Un tentativo "spartano" potrebbe essere quello di non enumerare ogni volta gli items, ma cancellare 200 items per volta:
putroppo i tempi non cambiano, ogni volta che si elimina un items la collection $l.items viene aggiornata e quindi i tempi sono simili al caso precedente.
Un approccio migliore è quello di cancellare gli item in batch da 5000 elementi tramite il metodo ProcessBatchData:
In pratica va costruito un xml con le url dei file/item da cancellare e poi farli eseguire in batch da SharePoint, i tempi per 5000 elementi sono dell'ordine dei secondi e l'operatività di SharePoint rimane invariata, non si blocca.
L'xml generato da passare al comando ProcessBatchData è simile al seguente:
PowerShell
$url = "http://sharepoint.sgart.local/sites/test"
$listName = "Workflow History"
$w=Get-SPWeb $url
$l=$w.Lists[$listName]
while($l.Items.Count -gt 0){
write-host "." -nonewline; $l.Items[0].Delete()
}
La lentezza è data dell'enumerazione $l.items che carica in memoria tutti gli items. Un altro effetto collaterale era il blocco di SharePoint durante la cancellazione.
Un tentativo "spartano" potrebbe essere quello di non enumerare ogni volta gli items, ma cancellare 200 items per volta:
PowerShell
while($l.Items.Count -gt 0){
$items = $l.Items
(200..0) | %{ write-host "." -nonewline; $items[$_].Delete() }
}
Un approccio migliore è quello di cancellare gli item in batch da 5000 elementi tramite il metodo ProcessBatchData:
PowerShell
$url = "http://sharepoint.sgart.local/sites/test"
$listName = "Workflow History"
$itemsMax = 5000 # numero di elementi da cancellare ad ogni batch
$w=Get-SPWeb $url
$l=$w.Lists[$listName]
$id = $l.id
$command = @"
<Method>
<SetList Scope="Request">$id</SetList>
<SetVar Name="ID">{0}</SetVar>
<SetVar Name="owsfileref">{1}</SetVar>
<SetVar Name="Cmd">Delete</SetVar>
</Method>
"@
$p=0
$items =$l.items
write-host "Counting... (batch nr. $p items $itemsMax)"
$c=$items.count
while($c -gt 0){
$i =0
write-host "Count $c"
$sb=new-object System.Text.StringBuilder
[void]$sb.Append("<?xml version=`"1.0`" encoding=`"UTF-8`"?><Batch>")
# cotruisco l'xml con le url dei file/item da cancellare
while ($i -lt $itemsMax -and $i -lt $c){
#write-host "." -nonewline
[void]$sb.AppendFormat($command, $items[$i].ID.ToString(),$items[$i]["FileRef"].ToString());
$i++
}
[void]$sb.append("</Batch>")
$exec = $sb.ToString()
write-host "Deleting..."
$d1 = (get-date)
# eseguo la cancellazione in batch
$r = $w.ProcessBatchData($exec)
$d2 = (get-date)
#rileggo per sicurezza
$w=Get-SPWeb $url
$l=$w.Lists[$listName]
$items =$l.items
write-host "Counting end..."
$c1=$items.count
write-host "Finish $c - $c1 | $d1 - $d2 | $($l.title)"
write-host " "
$c=$c1
$p++
}
Gli elementi cancellati finiscono nel cestino
L'xml generato da passare al comando ProcessBatchData è simile al seguente:
XML
<?xml version=`"1.0`" encoding=`"UTF-8`"?>
<Batch>
<Method>
<SetList Scope="Request">a20efd5b-5f51-4bb5-9715-0e2653fe60ce</SetList>
<SetVar Name="ID">1</SetVar>
<SetVar Name="owsfileref">/Lists/Workflow History/1_.000</SetVar>
<SetVar Name="Cmd">Delete</SetVar>
</Method>
<Method>
<SetList Scope="Request">a10edd6b-5f51-4ab5-9105-0e1653fd60ce</SetList>
<SetVar Name="ID">3</SetVar>
<SetVar Name="owsfileref">/Lists/Workflow History/3_.000</SetVar>
<SetVar Name="Cmd">Delete</SetVar>
</Method>
...
...
...
</Batch>