2014-11-07 3 views
5

Ho due tabelle di origine e diverse dozzine di pivot basate su di esse.Sincronizzazione delle affettatrici

C'è un campo comune alle due tabelle con un insieme comune di valori possibili.

Ho due affettatrici (una per tabella di origine). Ogni slicer controlla un numero di tabelle pivot associate.

Mi piacerebbe essere in grado di sincronizzarli.

Questo è se l'utente seleziona il valore A in Slicer_1, Slicer_2 viene aggiornato automaticamente per avere anche il valore A selezionato.

Quindi quello che ho finora è molto semplice

ActiveWorkbook.SlicerCaches("Slicer_1").SlicerItems("A").Selected = ActiveWorkbook.SlicerCaches("Slicer_2").SlicerItems("A").Selected ActiveWorkbook.SlicerCaches("Slicer_1").SlicerItems("B").Selected = ActiveWorkbook.SlicerCaches("Slicer_2").SlicerItems("B").Selected ActiveWorkbook.SlicerCaches("Slicer_1").SlicerItems("C").Selected = ActiveWorkbook.SlicerCaches("Slicer_2").SlicerItems("C").Selected

Ora come avrei ottenuto circa innescare automaticamente quando cambia slicer_1? Ho assegnato la macro a slicer_2, ma l'aggiornamento non si verifica fino a quando non si fa clic sulla casella dell'affettatrice.

E come posso ritardare l'esecuzione fino a quando tutte le modifiche sono state applicate. In questo momento è aggiorna il campo A (selezionato sì/no) aggiorna i miei tavoli e passa alla B ed ecc

voglio che aspettare con l'aggiornamento fino a quando tutti i campi affettatrice sono stati aggiornati

Grazie si

+1

Hai visto [questo] (http://www.jkp-ads.com/articles/slicers04.asp) –

+0

Anche se @SiddharthRout postato un link che forse risolve il problema, è possibile provare la sincronizzazione * Affettatrici * semplicemente sincronizzando una tabella * Pivot * collegata di ciascuna. Ho pubblicato un esempio che spero ti dia l'idea. – L42

risposta

4

La sincronizzazione delle affettatrici può essere eseguita in modo generico.
Con "generico" intendo che non ci dovrebbero essere dipendenze sui nomi di cache slicer (letterali) e la sincronizzazione potrebbe iniziare da qualsiasi cache di slicer.

L'approccio per risolvere questo problema è salvare lo stato di tutti gli oggetti della cache slicer. Dopo una modifica in una tabella pivot (sottostante una o più cache di slicer) i nuovi stati possono essere confrontati con i vecchi stati e le cache aggiornate riconosciute. Da lì la sincronizzazione può essere compiuta.

La mia soluzione si compone di 4 fasi:
1) Creare clsWrapperCache, una classe wrapper per Excel SlicerCache oggetto
2) creare clsWrapperCaches, una classe collezione di clsWrapperCache oggetti
3) creare clsCacheManager, una classe di manager per trattare con SlicerCache oggetto afferma
4) ThisWorkbook, impostando le chiamate al gestore

1) clsWrapperCache, classe wrapper di oggetti di Excel SlicerCache

' wrapper class around Excel SlicerCache object 
Option Explicit 
Public Object As SlicerCache 
Public OldState As String 

Public Function CurrentState() As String 
    ' state is set by: 
    ' a) name of first visible slicer item 
    ' b) number of visible slicer items 
    Dim s As String 

    If Object.VisibleSlicerItems.Count > 0 Then 
     s = Object.VisibleSlicerItems.Item(1).Name 
    Else 
     s = "" 
    End If 
    s = s & vbCrLf ' separator that cannot be found in a SlicerItem name 
    s = s & CStr(Object.VisibleSlicerItems.Count) 

    CurrentState = s 
End Function 

clsWrapperCache contiene un oggetto SlicerCache di Excel.
Ancora più importante: può amministrare lo stato di un SlicerCache. Stato Ottenere può essere fatto molto velocemente, ossia concatenando:

  • il nome del 1 ° VisibleSlicerItem e
  • il numero di VisibleSlicerItems.

OldState è inizialmente impostato nel Set_Caches di routine (fase 3) e può essere ripristinato in de Synchronize_Caches di routine (fase 3) se la cache affettatrice è stato coinvolto nel processo di sincronizzazione.

2) clsWrapperCaches, classe di raccolta di oggetti clsWrapperCache

' clsWrapperCaches, collection class of clsWrapperCache objects 
Option Explicit 

Private mcol As New Collection 

Public Sub Add(oWC As clsWrapperCache) 
    mcol.Add oWC, oWC.Object.Name 
End Sub 

Public Property Get Item(vIndex As Variant) As clsWrapperCache 
    ' vIndex may be of type integer or string 
    Set Item = mcol(vIndex) 
End Property 

Public Property Get Count() As Integer 
    Count = mcol.Count 
End Property 

Questa è una semplice classe di raccolta, semplicemente tiene clsWrapperCache oggetti. Verrà utilizzato per contenere oggetti nella raccolta AllCaches.

3) clsCacheManager, classe per trattare con oggetti stati SlicerCache

Option Explicit 

Public AllCaches As New clsWrapperCaches 

Public Sub Set_Caches() 
    Dim sc As SlicerCache 
    Dim oWC As clsWrapperCache 
    Dim i As Integer 

    If Me.AllCaches.Count <> ThisWorkbook.SlicerCaches.Count Then 
     ' a) on Workbook_Open event 
     ' b) maybe the user has added/deleted a Slice Cache shape by hand 
     Set AllCaches = New clsWrapperCaches 
     For Each sc In ThisWorkbook.SlicerCaches 
      'create a wrapper SlicerCache object 
      Set oWC = New clsWrapperCache 
      Set oWC.Object = sc 
      'save current state of SlicerCache into OldState 
      oWC.OldState = oWC.CurrentState 

      ' add wrapper object to collection 
      AllCaches.Add oWC 
     Next 
    End If 
End Sub 

Sub Synchronize_Caches() 
    ' copy current selections from slicer caches "FromCaches" into any other slicer cache with same SourceName 
    On Error GoTo ErrEx 
    Dim oWCfrom As clsWrapperCache 
    Dim oWCto As clsWrapperCache 
    Dim scFrom As SlicerCache 
    Dim scTo As SlicerCache 
    Dim si As SlicerItem 

    Dim i As Integer 
    Dim j As Integer 

    Application.EnableEvents = False ' prevent executing Workbook_SheetPivotTableUpdate event procedure 
    Application.ScreenUpdating = False 

    For i = 1 To Me.AllCaches.Count 
     Set oWCfrom = Me.AllCaches.Item(i) 
     If oWCfrom.CurrentState <> oWCfrom.OldState Then 
      Set scFrom = oWCfrom.Object 
      For j = 1 To Me.AllCaches.Count 
       Set oWCto = Me.AllCaches.Item(j) 
       Set scTo = oWCto.Object 

       ' Debug.Print oWCto.Name 
       If scTo.Name <> scFrom.Name And scTo.SourceName = scFrom.SourceName Then 
        scTo.ClearAllFilters ' triggers a Workbook_SheetPivotTableUpdate event 
        On Error Resume Next 
         For Each si In scFrom.SlicerItems 
          scTo.SlicerItems(si.Name).Selected = si.Selected 
         Next 
        On Error GoTo 0 

        ' update old state of wrapper object oWCto 
        oWCto.OldState = oWCto.CurrentState 
       End If 
      Next 
      ' update old state of wrapper object oWCfrom 
      oWCfrom.OldState = oWCfrom.CurrentState 
     End If 
    Next 

Ex: 
    Application.EnableEvents = True 
    Application.ScreenUpdating = True 
    Exit Sub 
ErrEx: 
    MsgBox Err.Description 
    Resume Ex 
End Sub 

Classe clsCacheManager gestisce gli stati di cache con metodi Set_Caches e Synchronize_Caches.
Set_Caches: se il numero di cache in ThisWorkbook è diverso da quello di AllCaches, la raccolta AllCaches viene (ri) creata. Con la presente il OldState di ogni cache di slicer viene salvato.

Synchronize_Caches: tutte le cache sono attraversate qui. Se una cache di slicer è stata aggiornata (oWCfrom.CurrentState <> oWCfrom.OldState) rispetto a qualsiasi altra cache con lo stesso SourceName (ad esempio "anno") verrà aggiornata. L'aggiornamento è copiando tutte le selezioni di elementi slicer dalla cache di origine alla cache di destinazione. OldState per tutte le cache interessate viene ripristinato allo stato corrente al termine del processo di sincronizzazione.

4) ThisWorkbook, impostando le chiamate al gestore della cache

Option Explicit 
Private mCacheManager As New clsCacheManager 

Private Sub Workbook_Open() 
    SetCacheManager 
    mCacheManager.Set_Caches 
End Sub 

Private Sub Workbook_SheetPivotTableUpdate(ByVal Sh As Object, ByVal Target As PivotTable) 
    SetCacheManager 
    mCacheManager.Set_Caches 
    mCacheManager.Synchronize_Caches 
End Sub 

Private Sub SetCacheManager() 
    If mCacheManager Is Nothing Then 
     Set mCacheManager = New clsCacheManager 
    End If 
End Sub 

benefici Alle dal punti da 1 a 3 possono essere sfruttati al punto 4: possiamo fare chiamate a CacheManager come SetCaches o Synchronize_Caches. Questo codice è facile da leggere.

I vantaggi di questa soluzione:

  1. funziona per tutte le cache affettatrice in una cartella di lavoro
  2. non dipende da nomi SlicerCache
  3. molto veloce, perché gli stati degli oggetti di cache affettatrice si ottengono molto veloce
  4. estendibile. La classe clsCacheManager potrebbe essere estesa per gestire le dipendenze tra le cache di slicer.
+0

Per quanto riguarda il punto 3, temo che in realtà sarà molto lento su grandi perni. Ho scritto due post in precedenza che trattano il problema di velocità e forniscono soluzioni alternative: http://dailydoseofexcel.com/archives/2013/11/14/filtering-pivots-based-on-external-ranges/ http://dailydoseofexcel.com/archives/2015/11/17/filtering-pivottables-with-vba-deselect-slicers-first/ L'implementazione di un approccio basato su tali post potrebbe accelerare significativamente l'esecuzione. E sto parlando di minuti, non di millisecondi. – jeffreyweir

1

mi si avvicinò con lo stesso problema in passato e, a mio parere, la sincronizzazione tabella pivot è più facile che filtri dei dati .Quando si collega diversi tabelle Pivot (con stessa cache) in un affettatrice , alterando qualcuno di quelli tabelle pivot di campo (da cui è stato creato il affettatrice) cambia la affettatrice Selezione così come il resto delle tabelle pivot .

Quindi, ad esempio, sono disponibili 12 tabelle pivot e 2 affettatrici, 6 assegnate a 1 e altre 6 assegnate all'altra.
anche permesso di dire che avete un campo comune Workweek con gli stessi elementi presenti in tutte le tabelle pivot, si può provare qualcosa di simile:

Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) 
    On Error GoTo halt 
    Application.EnableEvents = False 
    Application.ScreenUpdating = False 
    Dim ww As String, pF1 As PivotField, pF2 As PivotField 
    Set pF1 = Me.PivotTables("PT1").PivotFields("WorkWeek") 
    Set pF2 = Me.PivotTables("PT2").PivotFields("WorkWeek") 
    Select Case True 
    Case Target.Name = "PT1" 
     ww = pF1.CurrentPage 
     If pF2.CurrentPage <> ww Then pF2.CurrentPage = ww 
    Case Target.Name = "PT2" 
     ww = pF2.CurrentPage 
     If pF1.CurrentPage <> ww Then pF1.CurrentPage = ww 
    End Select 
forward: 
    Application.EnableEvents = True 
    Application.ScreenUpdating = True 
    Exit Sub 
halt: 
    MsgBox Err.Number & ": " & Err.Description 
    Resume forward 
End Sub 

Hai messo questo codice nel foglio che contiene la vostra Tabelle pivot target (PT1 e PT2 nell'esempio sopra).
Prendere nota dei seguenti presupposti per questo esempio:

  1. PT1 e PT2 hanno settimana lavorativa campo su Filtro report (non righe/colonne).
  2. PT1 è collegato a Slicer1 e PT2 è collegato a Slicer2.
  3. Non è consentita alcuna selezione multipla (almeno per quanto sopra impostato).

Quindi, in pratica ciò che accade è quando si cambia selezione settimana lavorativa PT1 che è legata al Slicer1,
PT2 cambia e che a sua volta cambia la selezione Slicer2 pure.
Se si modifica la selezione Slicer1 o 2, si verificherà lo stesso effetto.
Qualsiasi modifica alla selezione in Slicer1 avrà effetto su Slicer2.
Questa è solo l'idea. Non so se stai mettendo campi su Filtro report o Righe/Colonne.
È possibile regolare il campione specificato in base alle proprie esigenze per ogni evenienza.
Per selezionare più elementi, sarà necessario utilizzare un ciclo per assegnare e selezionare ciascuno degli elementi. HTH.

+0

Ho bisogno di più selezioni per essere consentito. Grazie. – Ben

+0

Questo in effetti sarà molto più veloce, anche se, come aggiunge Ben, non aiuta l'OP in questo particolare scenario. Ma nello scenario "single select", vince su velocità e semplicità a mani basse. – jeffreyweir

0

ho finito per usare questo codice:

Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) 
    Dim sc1 As SlicerCache 
    Dim sc2 As SlicerCache 
    Dim si1 As SlicerItem 

    Set sc1 = ThisWorkbook.SlicerCaches("Slicer_Cache1") 
    Set sc2 = ThisWorkbook.SlicerCaches("Slicer_Cache2") 

    Application.ScreenUpdating = False 
    Application.EnableEvents = False 

    sc2.ClearManualFilter 

    For Each si1 In sc1.SlicerItems 
      sc2.SlicerItems(si1.Name).Selected = si1.Selected 
    Next si1 


    MsgBox "Update Complete" 

    clean_up: 
     Application.EnableEvents = True 
     Application.ScreenUpdating = True 
     Exit Sub 

    err_handle: 
     MsgBox Err.Description 
     Resume clean_up 
    End Sub 

E 'legato come un trigger di aggiornamento per una delle mie tabelle pivot.

+0

Ben: per curiosità, quanti elementi ci sono in ogni Slicer, e quanto tempo ci vuole per eseguire? – jeffreyweir

+0

11 articoli per affettatrice. Ci vogliono circa 10 secondi per aggiornare. Le tabelle di origine che alimentano i perni avevano circa 90.000 file ciascuna. – Ben

+0

Se si imposta il.Proprietà ManualUpdate per ogni tabella pivot su TRUE prima della riga sc2.ClearManualFilter, quindi impostarli nuovamente su FALSE prima della riga "Aggiornamento completato" di MsgBox, potrebbe accelerare significativamente. Sarei molto curioso di sapere se lo fa. – jeffreyweir

0

Ho usato il codice qui sotto. Aggiunge anche i nomi selezionati sull'affettatrice ai nomi di campo "Intestazione" che faccio riferimento ai titoli della tabella pivot.

Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) 
Dim pi As PivotItem 
Dim dest As PivotField 

    If Target.Name = "PivotMPM" Then 
     Application.EnableEvents = False 
     Application.ScreenUpdating = False 
     Application.Calculation = xlCalculationManual 
     Set dest = PivotTables("PivotHW").PivotFields("IT Region") 
     On Error GoTo What_Happened 
     Range("Header") = "" 
    ' You cannot select NOTHING, so first go and turn on the ones we want, then go and turn off the others! 
     For Each pi In Target.PivotFields("IT Region").PivotItems ' Now we set them the same as the other one! 
     If pi.Visible And dest.PivotItems(pi.Name).Visible = False Then 
      dest.PivotItems(pi.Name).Visible = pi.Visible 
     End If 
     If pi.Visible Then 
      Range("Header") = Range("Header") & pi.Name & ", " 
     End If 
     Next pi 
     Range("Header") = Left(Range("Header"), Len(Range("Header")) - 2) 
     For Each pi In Target.PivotFields("IT Region").PivotItems ' Now we set them the same as the other one! 
     If pi.Visible <> dest.PivotItems(pi.Name).Visible Then 
      dest.PivotItems(pi.Name).Visible = pi.Visible 
     End If 
     Next pi 
    End If 
    Done: 
    Application.EnableEvents = True 
    Application.Calculation = xlCalculationAutomatic 
    Application.ScreenUpdating = True 
    Exit Sub 

What_Happened: 
MsgBox Err.Description 
GoTo Done 
End Sub