wtorek, 31 marca 2015

SQL w Excelu Cz1: Formuła =SQL(tabela, where_war1;where_war2)

Niekiedy musimy w Excelu poradzić sobie z dużą ilością połączeń między tabelami albo agregować dane z setek tysięcy rekordów uwzględniając wiele warunków. Powstają wtedy arkusze monstra z setkami tysięcy formuł, które przeliczają się długo. Można obliczenia wyłączyć narażając się na nieaktualne dane. Można też skorzystać z Pivota ale ma on ograniczone możliwości. Aż prosi się, by skorzystać z SQL i silnika bazodanowego, który lepiej radzi sobie z agregowaniem i łączeniem tabel. Ale użytkownicy oczekują od nas formatki w Excelu, nie używają albo nie umieją obsłużyć aplikacji bazodanowych. Co robić?

1. Potrzebny jest MS Access runtime, nic nie kosztuje, do ściągnięcia tutaj: najnowszy: http://www.microsoft.com/pl-pl/download/details.aspx?id=39358, może być starszy (2010, 2007)

2. W edytorze VBA Excela należy w menu Tools/References włączyć Microsoft ActiveX Data Objects x.x Library. Jeśli jest kilka można wybrać najnowszą wersję.

3. A oto funkcja użytkownika (UDF), która na zaznaczonej w Excelu tabeli wykonuje kwerendę SQL. Możemy stworzyć kilka takich formuł, odpowiednio je nazywając. Kod wklejamy do nowego modułu w edytorze VBA do którego dostaniemy się wciskając Alt+F11. Jest to funkcja tablicowa, tj. należy zaznaczyć więcej komórek by pomieścić wynik zapytania SQL, wpisać formułę i zatwierdzić wciskając Ctrl+Shift+Enter.  Liczbę parametrów można łatwo zmienić pamiętając o prawidłowym przypisaniu typu zmiennym i prawidłowym "wstrzyknięciu" ich do łańcucha tekstowego zapytania SQL - można np. dodać drugi zakres danych a w zapytaniu SQL dodać połączenie tabel LEFT JOIN - by łączyć tabele szybciej niż za pomocą VLOOKUPa.

Function SQL(dataRange As Range, CritA As String, CritB As Double) As Variant

    Function SQL(dataRange As Range, CritA As String, CritB As Double) As Variant
    Application.Volatile
    
    Dim cn As ADODB.Connection
    Dim rs As ADODB.Recordset
    Dim currAddress As String
    Dim varHdr, varDat, contentOut As Variant
    Dim nc, nr, i, j As Long
    
    SQL = Null
    
    currAddress = ActiveSheet.Name & "$" & dataRange.Address(False, False)
    
    strFile = ThisWorkbook.FullName
    strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile _
    & ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=0"";"
    
    Set cn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
   
    rs.CursorLocation = adUseClient ' required to return the number of rows correctly
    cn.Open strCon
    
    strSQL = "SELECT * FROM [" & currAddress & "]" & _
             "WHERE [A] =  '" & CritA & "' AND [B] >= " & CritB & " " & _
             "ORDER BY 10 DESC"
    
    rs.Open strSQL, cn
    
    'Check if recordset is empty
    If rs.EOF Then
        MsgBox "Function does not return any values"
        SQL = ""
        Exit Function
    End If
    
    
    ' Process Column Headings
    nc = rs.Fields.Count
    ReDim varHdr(nc - 1, 0)
    For i = 0 To rs.Fields.Count - 1
        varHdr(i, 0) = rs.Fields(i).Name
    Next

    ' Get Rows from the Recordset
    nr = rs.RecordCount
    varDat = rs.GetRows

    ' Combing Header and Data and Transpose

    ReDim contentOut(0 To nr, 0 To nc - 1)
    For i = 0 To nc - 1
        contentOut(0, i) = varHdr(i, 0)
    Next

    
    
    
    For i = 1 To nr
        For j = 0 To nc - 1
           contentOut(i, j) = varDat(j, i - 1)
            
            
                
        Next
    Next

  ' Optional solution: Write Output Array to Sheet2
  '  With Sheet2
  '      .Cells.Clear
  '      .Range("A1").Resize(nr, nc) = contentOut
  '  End With
      
      
    'Figure out size of calling range which will receive the output array
    Dim nRow As Long: nRow = Application.Caller.Rows.Count
    Dim nCol As Long: nCol = Application.Caller.Columns.Count

    'Error if calling range too small
    If nRow < UBound(contentOut, 1) Or nCol < UBound(contentOut, 2) Then
        'Popup message
        'MsgBox "your range is too small."
        ' or return #VALUE! error
        SQL = "Too small range" 'CVErr(xlValue)
        ' or both or whatever else you want there to happen
        Exit Function
    End If

    'Initialise output array to match size of calling range
    Dim varOut As Variant
    ReDim varOut(1 To nRow, 1 To nCol)
    'And fill it with some background value
    Dim iRow As Long
    Dim iCol As Long
    For iRow = 1 To nRow
    
        For iCol = 1 To nCol
            varOut(iRow, iCol) = ""   ' or "funny bear", or whatever
        Next
    Next

    'Put content in output array and return
    For iRow = 0 To UBound(contentOut, 1)
        For iCol = 0 To UBound(contentOut, 2)
            varOut(iRow + 1, iCol + 1) = contentOut(iRow, iCol)
        Next
    Next
      
      
      
      SQL = varOut
    
    'Cleanup
    Erase contentOut
    Erase varHdr
    Erase varDat
    
    rs.Close
    Set rs = Nothing
    Set cn = Nothing


End Function


Przykładowy plik tutaj.

Źródła/Credits:
Makro poniższe jest jedynie moim skromnym rozwinięciem rozwinięciem  pytania innego użytkownika na forum Stackoverflow: "Performing SQL queries on an Excel Table within a Workbook with VBA Macro".
Skorzystałem z podpowiedzi na forum MrExcel jak zwrócić tablicę (array) z recordset'u http://www.mrexcel.com/forum/excel-questions/842976-excel-udf-return-array-ace-sql-recordset.html#post4104616 dzięki  skorzystąłem też pomocy m.in. Jean-François Corbett'a (wskazanie błędu 'za mały zakres' i wypełnienie pustymi nadmiarowych komórek zamiast "#N/A")

piątek, 27 marca 2015

Excel to dataset...dataset to Excel

Na razie zbieram ciekawe linki:
http://stackoverflow.com/questions/14261655/best-fastest-way-to-read-an-excel-sheet-into-a-datatable

Biblioteka easyXLSX: http://www.easyxls.com/manual/FAQ/import-excel-to-datatable.html
http://www.codeproject.com/Questions/376355/import-MS-Excel-to-datatable
http://csharp.net-informations.com/excel/csharp-excel-oledb.htm

Dataset 2 Excel
http://stackoverflow.com/questions/5790431/export-data-from-dataset-to-excel
http://www.codeproject.com/Articles/11171/DataSet-to-Excel-in-Two-steps-with-Different-Style
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ea02f8e8-edf5-4aca-8c74-ef3a94b5328d/export-dataset-into-excel-sheet-in-c?forum=csharpgeneral

ExcelDNA <-> Access
http://www.mikesdotnetting.com/article/79/import-data-from-excel-to-access-with-asp-net
https://smurfonspreadsheets.wordpress.com/category/excel/page/6/

Linq!
https://code.google.com/p/linqtoexcel/
https://msdn.microsoft.com/en-us/library/dd920313%28v=office.12%29.aspx
http://blogs.msdn.com/b/ericwhite/archive/2008/11/14/using-linq-to-query-excel-tables.aspx
http://dennis.bloggingabout.net/2008/02/29/getting-data-from-excel-the-fast-way-using-linq/

Update:
Linq:
http://geekswithblogs.net/CodeSpeaker/archive/2009/10/04/using-linq-with-excel-sheets.aspx
http://dennis.bloggingabout.net/2008/02/29/getting-data-from-excel-the-fast-way-using-linq/
http://www.nathanpjones.com/wp/2013/06/query-excel-file-using-linq/

Chyba najlepsze:
http://stackoverflow.com/questions/1485958/read-excel-using-linq
https://vimeo.com/7689508



środa, 18 marca 2015

Power!!! - Excel + Csharp + R = funkcje R w Excelu

 Update: Działający kod C# umożliwiający rozpoczęcie eksperymentów z funkcjami R w excelu znajdziemy (najbardziej aktualny) tutaj: https://github.com/Excel-DNA/Samples/tree/master/UsingRDotNet (poprosiłem wcześniej o przykład  za pośrednictwem stackexchange.). Uwaga: podczas instalacji R należy zaznaczyć opcję zapisania wersji R do rejestru Windows, ponieważ program korzysta z tego wpisu by ustalić lokalizację binariów R.


Starszy tekst:
Polecam wpis opisujacy jak szybko uzyskac mozliwosc korzystania z funkcji napisanej w C# i pakietu statystycznego R w Excelu. Chyba prosciej nie mozna: http://mockquant.blogspot.com/2011/07/yet-another-way-to-use-r-in-excel-for.html (update: należy wprowadzić zmiany opisane tutaj:
https://groups.google.com/forum/#!topic/exceldna/7_wr8pwuCZ0 )

Dodatkowo, prosty opis uzycia ExcelDNA tutaj (rozdzial "XLL Add-Ins Method") http://www.adamtibi.net/07-2012/using-c-sharp-net-user-defined-functions-udf-in-excel
http://www.brad-smith.info/blog/archives/4

Dane trzeba pobrac i wstepnie zawezic:
moze skorzystac z tego helpa? DbConnection, DbCommand and DbException : https://msdn.microsoft.com/pl-pl/library/9hy8csk1%28v=vs.110%29.aspx 
https://sysmod.wordpress.com/2012/02/06/from-vba-to-vb-net-using-exceldna/
https://groups.google.com/forum/#!forum/exceldna



poniedziałek, 9 marca 2015

Nie zmienisz całego świata, ale... (minimalist)

http://mnmlist.com/small-changes/

(przeczytane, potem zapomniane, potem nie mogłem znaleźć...)
mnmlist: the only thing you can change

You can’t change your entire life.

You can only change your next action.

—

You can’t change a relationship with a loved one.

You can only change your next interaction.

—

You can’t change your entire job.

You can only change your next task.

—

You can’t change your body composition.

You can only change your next meal.

—

You can’t change your fitness level.

You can only start moving.

—

You can’t declutter your entire life.

You can only choose to get rid of one thing, right now.

—

You can’t eliminate your entire debt.

You can only make one payment, or buy one less unnecessary item.

—

You can’t change the past, or control the future.

You can only change what you’re doing right now.

—

You can’t change everything.

You can only change one, small thing.

And that’s all it takes. 

Go Bulgaria!!! Darmowy podręcznik C#

Polecam: http://www.introprogramming.info/english-intro-csharp-book/

Publikacja jest też dostępna online plus mat. video i prezentacje: http://www.introprogramming.info/english-intro-csharp-book/read-online/

środa, 4 marca 2015

Yonguo - obiektywy do Canon

Warto chyba obserwować tańsze a pewnie równie dobre obiektywy do Canona:
http://www.fotopolis.pl/n/20149/yongnuo-35mm-f2-kolejny-obiektyw-w-ofercie-producenta/

Excel: Nie ma kontrolki kalendarza? Pop-up kalendarz bez kontrolki!

W Excelu 2010 nie ma kontrolki kalendarza (Month View). Można ją samemu sobie doinstalować, ale gdy prześlemy komuś nasz plik, okaże się, że nie nasz wyskakujący kalendarz nie będzie działać. Polecam rozwiązania bez kontrolki: https://sites.google.com/site/e90e50/calendar-control-class#TOC-UPDATES (mój wybór!) oraz http://shutupdean.com/blog/2014/09/18/vba-calendar-userform-control/

W pierwszym przypadku skorzystałem z pliku Calendar_class_date picker only. Aby ograniczyć działanie makra do konkretnej kolumny, tworzę obiekt tabelę (Ctrl+t) a kodzie zakładki (kodzie zdarzeń dotyczących konkretnego arkusza wpisuję :
 If Not Intersect(Target, Me.Range("Table1[Date]")) Is Nothing Then
(tu kod  autora)
End If

wtorek, 3 marca 2015

The Lord Is My Shepherd — Eastman Johnson


Spodobał mi się ten obraz. Przyszedł mi na myśl podczas obserwowania na G+ bicia piany nt. czy kreacjonizm, czy naukowy darwinizm ze wszystkimi związanymi z tematem przesądami i zabobonami zwolenników nauki (cóż to jest? jak definiowanej?).