Skip to main content

Große Listen auf Sharepoint löschen

Erstellt von Robert Fink | |   Sharepoint | SharepointListenPowershellListenschwellenwert

Ausgangssituation

Neulich hatten wir folgende Problemstellung bei einem Kunden. Eine Lösung von uns erzeugte in einer eigenen History Liste massiv Einträge, die nie aufgeräumt wurden. Was dann dazu führte, das bei dem Kunden der Speicherplatz zur Neige ging obwohl er ständig aufräumte und fleißig nicht mehr benötigte Dateien löschte. Als wir uns das genauer angesehen haben waren in dieser History Liste bereits 7,5 Millionen Items. Jetzt ging es natürlich darum diese Liste irgendwie leer zu bekommen. Wer schon mal versucht hat wirklich große Listen auf dem SharePoint leer zu bekommen wies, das dies ein nicht ganz einfaches Unterfangen ist.

Versuche auf der Oberfläche

Unsere ersten Versuche starteten wir auf der SharePoint Oberfläche. Da rennt man dann schon in die ersten Probleme. Als erstes bekommt man ja schon Probleme die Liste überhaupt angezeigt zu bekommen. Der magische 5000 Elemente Listenschwellenwert schlägt da schon mal erbarmungslos zu. Da kommt man mit verschiedenen Ansätzen aber drum rum. Wie werde ich vielleicht mal in einem eigenen Artikel behandeln. Kurz und gut wir haben dann für die Liste das Throttling komplett abgeschaltet. Aber 7,5 Millionen Items über die Oberfläche zu löschen ist nicht wirklich ein gangbarer Weg. Es sei denn man hat einen Praktikanten der nichts anderes zu tun hat und keine Probleme mit stumpfsinnigen Aufgaben hat. Problem hier ist aber vor allem, dass es da auch ewig dauert.

Ein Ansatz aus den Google Ergebnissen war auch die Liste per Access zu öffnen und da zu löschen. Das hat aber auch nicht funktioniert, das Access zwar irgendwas gemacht hat aber selbst nach langer Wartezeit die Liste nicht öffnete.

Versuche direkt auf dem Server

Also ab auf den Server. Wir sind ja schließlich Admins.

SharePoint Manager

Erster Ansatz war der SharePoint Manager. Hier hatten wir die Idee die Liste irgendwie leeren zu können. Schlechte Idee kann ich gleich sagen in dem Moment in dem ich Liste selektiert hatte legte das Ding los die Liste auszulesen was dazu führte, dass die ganze Seite nicht mehr erreichbar war und auch hier passierte selbst nach langer Wartezeit nicht wirklich etwas.

Powershell

Also dann doch Powershell. Was ja sowieso der beste Ansatz ist.

Einfache Scripte

Natürlich sucht man erst mal auf Google und findet da ja auch genügend Artikel. Meine persönliche Erfahrung zeigte aber, dass  die hier angebotenen Lösungen mit so großen Listen nie bearbeitet wurden. Der Ansatz war immer eine Kollektion der Listen Items zu erzeugen und diese mit Delete zu löschen. Im Prinzip ja aber! Eine Kollektion von 7,5 Millionen Items zu erzeugen dauert echt lange und auch hier haben wir nach langen Wartezeiten abgebrochen weil mehr oder weniger nichts passiert.

Zwischenlösung

Also wie dann? Man muss die Zahl der Items in der Kollektion irgendwie begrenzen. Mein erster Ansatz war folgender.

Add-PSSnapin Microsoft.Sharepoint.Powershell
$web = Get-SPWeb Adresse_der_Seite
$list = $web.Lists[„Listenname“]

$spQuery = New-Object Microsoft.SharePoint.SPQuery
$spQuery.ViewAttributes = "Scope='Recursive'";
$spQuery.RowLimit = 2000
$caml = '<OrderBy Override="TRUE"><FieldRef Name="ID"/></OrderBy>' 
$spQuery.Query = $caml 

do
{ 
    $listItems = $list.GetItems($spQuery)
    $spQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
    for($i = 0; $i -lt $listItems.Count; $i++ )
    {
        $listItems[$i].Delete()
    }
}
while ($spQuery.ListItemCollectionPosition -ne $null)

Also erst mal eine SPQuery erzeugen. Wichtig dabei ist das RowLimit. Hier legt man fest wie viele Items die Query liefert. Das Ganze dann in eine Do While Schleife gepackt um das Script so lange laufen zu lassen wie noch Items in der Liste sind. Dabei ist die ListItemCollectionPosition wichtig. So lange mehr Items in der Liste sind wie die Query abruft bekommt man hier einen Wert. Erst wenn die Liste leer ist bekommt man hier ein null zurück.

Mit dem Script war es dann erst mal möglich täglich ungefähr doppelt so viele Items aus der Liste zu löschen wie täglich dazu kamen. Das lief eine Zeit lang ganz gut aber irgendwann wurde das Ganze langsamer und dann schafften wir es gerade noch die Liste nicht mehr massiv anwachsen zu lassen. Warum genau habe ich leider nicht herausgefunden.

Es lebe das Batch Processing

Also machte ich mich noch mal auf die Suche um einen effektiveren Weg zu finden Items zu löschen. Dabei bin ich dann irgendwann auf die Möglichkeit aufmerksam ein Batch Job zu erzeugen und den in einem Rutsch ausführen zu lassen. Das ganze sieht dann so aus.

Add-PSSnapin Microsoft.Sharepoint.Powershell
$website = get-spweb http://meinSharePoint.tld
$whl = $website.getlist("http://meinSharePoint.tld/Lists/Listenname“)
$listID = $whl.id.Guid
$spquery = New-Object Microsoft.Sharepoint.SpQuery
$spquery.RowLimit = 1000
$spquery.Query = "<query></query>"

do
{
  $delitems = $whl.GetItems($spquery)
  $spQuery.ListItemCollectionPosition = $delItems.ListItemCollectionPosition
  $batch = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><Batch>"
  foreach($ListItem in $delitems)
  {
    $batch = $batch + "<Method><SetList Scope=`"Request`">" + $listID + "</SetList><SetVar Name=`"ID`">" + $ListItem.id + "</SetVar><SetVar Name=`"Cmd`">Delete</SetVar></Method>"
  }
  $batch = $batch + "</Batch>"
  $website.ProcessBatchData($batch)
}
while ($spQuery.ListItemCollectionPosition -ne $null)

Im Prinzip ist die Vorgehensweise die gleiche. Erst eine Query erzeugen um die Anzahl der Items zu begrenzen die in einem Durchlauf bearbeitet werden. In der Do While Loop geht man dann aber anders vor. Man erzeugt eine xml das dann übergeben und abgearbeitet wird. Als erstes benötigt man den xml header. In einer foreach Schleife kann man dann für jedes Item eine Methode erzeugen. Dabei wird die Listen ID benötigt damit der Batch Verarbeitung weis auf welche Liste es die Methode anwenden soll. Zur eindeutigen Identifizierung der Items habe ich mich natürlich für die Item ID entschieden. Und dann muss man noch das Kommando das ausgeführt werden soll definieren. In meinem Fall natürlich Delete. Am Schluß muss man dann noch eine schließendes Batch Tag einfügen. Das Batch XML übergibt man dann der ProcessBatchData Methode. Auch hier habe ich wieder über die ListItemCollectionPosition ermittelt ob in der Liste noch Items vorhanden sind.

Mit der Query Größe muss man etwas rum probieren. Ich habe mich für 1000 Items entschieden. Damit dauerte das löschen der 1000 Items zwischen 40 - 60 Sekunden undicht konnte die Liste in 2 Tagen löschen ohne den SharePoint zu beeinträchtigen.

Ich weis nicht warum der SharePoint so lange braucht Items zu löschen. Auf dem SQL würde das Löschen der Items in sehr kurzer Zeit erledigt sein. 

Was man vielleicht noch beachten sollte und was vermutlich das Tempo auch beeinflusst ist der Papierkorb. Eigentlich sollte der Delete Befehl das Item direkt löschen und nicht in den Papierkorb verschieben. Dies war aber bei mir nicht der Fall. also vielleicht bei solchen Aktionen den Papierkorb deaktivieren. Das sollte man aber den Benutzern kommunizieren, die sich sonst eventuell auf dieses Feature verlassen. Alternativ muss man dann denn Papierkorb noch mal selbst löschen oder die Verweildauer im Papierkorb eben verkürzen.

Das Batch Processing ist sehr interessant. Hier könnte man sich auch durchaus komplexere Dinge zusammenstellen.

So vielleicht hilft das hier ja jemanden wirklich große SharePoint Listen los zu werden

Zurück