Printing all spreadsheet names only in a workbook - database

Is it possible to print ONLY the spreadsheets names (the names at the bottom on the tabs)?
I have a workbook with quite a lot of spreasheets (over 500, er, it's a catalogue of music... CDs, what's on them, composors, conductors, movements, times etc.) and would like to do a print out of just the names of, not all and every part of the workbook.
Yes I know I should more than likely do this in Access, but I started it on a Commidore 64 years ago and have just been 'converting' it as I go along. Latest version is in Excel 2007. So esentially it's a '2D database' done using spreadsheets, rather than a '3D database' done in Access.
Many regards.

Using VBA you can pretty easily dump the list of sheet names to a new sheet in your workbook.
Sub PrintSheetNames()
i=1
For Each sht in Sheets
Cells(i,1).Value = sht.Name
i=i+1
Next
End Sub
Go to any blank worksheet, and then run this macro. It will output all the worksheet names in the first column. You can then just print the list.

Assuming you using VBA and not .NET you could write a sub routine similar to this. The same objects and names are available in .NET.
Sub ShowTabNames()
Dim s As Worksheet
Dim tabs As String
For Each s In ActiveWorkbook.Worksheets
If Len(tabs) > 0 Then tabs = tabs & ", "
tabs = tabs & s.Name
Next
MsgBox tabs
End Sub

You can do this using Perl with Spreadsheet::ParseExcel module installed.
use Spreadsheet::ParseExcel;
use strict;
my $filename = shift || "Book1.xls";
my $e = new Spreadsheet::ParseExcel;
my $eBook = $e->Parse($filename);
my $sheets = $eBook->{SheetCount};
my ($eSheet, $sheetName);
foreach my $sheet (0 .. $sheets - 1) {
$eSheet = $eBook->{Worksheet}[$sheet];
$sheetName = $eSheet->{Name};
print "Worksheet $sheet: $sheetName\n";
}

You can make a OleDbConnection to your Excel workbook then call GetOleDbSchemaTable to get list of all tables which are nothing but list of excel sheets
ExcelConnection.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });

You can do this using Python with the xlrd module installed:
import xlrd, sys
filename = (sys.argv[1:2] or ['Book1.xls'])[0]
book = xlrd.open_workbook(filename, on_demand=True)
for name in book.sheet_names():
print name # name is in Unicode; may need encoding
Note: the on_demand arg is new in version 0.7.1; it allows saving time and memory by not parsing all the worksheets up front. If you have an earlier version, either upgrade or remove on_demand=True in the snippet above.

Related

Pull data from multiple closed workbooks and sheets in excel

I want to pull data from multiple workbooks and worksheets that are closed.
I'm able to extract the data by using the below formula:
=INDEX('C:\Users\Shubham\Downloads\Ex_Files_Excel_Macros_and_VBA_for_Beginners\Ex_Files_Excel_Macros_and_VBA_for_Beginners\Exercise Files\[03_01 Undo.xlsx]Attendance'!C:C, 6)
but I want to provide the path in a cell using CONCATENATE function let's say in E5 so that value in E5 comes like below:
'C:\Users\Shubham\Downloads\Ex_Files_Excel_Macros_and_VBA_for_Beginners\Ex_Files_Excel_Macros_and_VBA_for_Beginners\Exercise Files\[03_01 Undo.xlsx]Attendance'!C:C
So I want to use the formula like:
= INDEX(E5, 6)
where E5 has the path and range defined but this doesn't take the value in E5 as table array.
I can't use the indirect formula since it doesn't work with closed workbooks. I have tried to understand the VBA code but doesn't work out.
I would really appreciate it if someone can help me out to resolve this issue since I have to extract data from multiple workbooks and multiple sheets and have to use dynamic reference to path and range.
I had the same problem, wanting to extract data from multiple workbooks with a volatile worksheet address. Indirect and Concatenate worked, but only when source files were open. There is no function which would work with closed files.
A macro was the solution, which I found here;
https://www.mrexcel.com/board/threads/vba-to-pull-data-from-dynamically-named-closed-workbooks.984156/
My solution uses a list of the source file name components with a volatile date component (date is the only variable in the source file names) and a volatile worksheet component (I need to take data for a person, where each person's name is the name of a worksheet in each file).
That list looks like this;List of File Names
I and K (Name) are volatile. These are modified in my list by having these cells reference separate input cells to simplify a query. Enter the name and the latest payroll date you want to query. It looks back a set number of cycles.
The formula to get the data I want is =COUNTIF(INDIRECT(CONCATENATE(H4,I4,J4,K4,L4)),"S")
How many times does S appear in range D63:U63 in tab Moe for each file.
The macro solution uses the Date information in column I to open all the source files as follows (I have added comments to hopefully make it clearer what each step does);
Public Sub Macro_Name()
' Opens Timesheets files to allow "Indirect" formula to obtain data
Dim r As Long
' I don't know what this does, but it is necessary
Application.ScreenUpdating = False
' Stops screen updating to speed up operation
Calculate
' Calculates to clear any previous data in the file. File is set to manual calculation.
With ThisWorkbook.ActiveSheet
For r = 4 To .Cells(Rows.Count, "I").End(xlUp).Row
' Starting at row 4, uses data in column I in the command below until the end of the data. You can't have any other data in column I.
Workbooks.Open Filename:="S:\Payroll\Weekly Timesheets " & .Cells(r, "I") & ".xlsm"
' Opens each file called Weekly Timesheets [date].xlsm
Next
Windows("[The name of your working file].xlsm").Activate
Calculate
Range("B2").Select
' This calculates your working file and goes to a convenient "home" cell
End With
Dim wb As Workbook
For Each wb In Application.Workbooks
If Not wb Is ThisWorkbook Then
wb.Close SaveChanges:=False
End If
Next
' This sub routine closes all open Excel files EXCEPT your working file. Best not to have other files open as it is set NOT to save.
Application.ScreenUpdating = True
End Sub
I found this page to set the file for manual calculation, which I preferred.
https://excel.tips.net/T001988_Forcing_Manual_Calculation_For_a_Workbook.html
I'm not sure if this solution is efficient, and it is not perhaps as elegant as I would like, but it has worked for me. I hope it helps.
Big shoutout to John_w at MrExcel.com who provided the core code above. I just fiddled a few things so it worked for me.

Real-Time Database Updating

I have ~300 Identical Excel Spreadsheets (.xlsx) that all have ten different cells I would like to project to an Access database. Some are single cells, a few ranges-- all of them are named in each individual workbook. The database should hold all values from the same worksheet on the same line, and all numbers should update in real time.
My goal is to use access to keep a running record on the contents of these cells. I have attempted to link the workbooks to the Master Access database using: External Data->Import and Link->Excel->Link to the data source by creating a linked table-> and here I would select one of the named ranges and click 'Finish', but I can only do this once per spreadsheet. This makes repeating this process for all spreadsheets unfeasible.
Is there a way to use Access VBA to create a linked Excel Table for each Workbook in the folder?
I am sort of teaching myself Access, and am still relatively new at it so any insight will help.
Cheers.
Luckily, I had just that lying around somewhere. This links all excel files in the same folder as the database.
Note that this just uses the file name as the tablename, and links everything using default settings. Change the DoCmd.TransferSpreadsheet line to customize it. Originally I have this linked to a form so I can choose what to import, and how it is handled.
Public Sub ImportExcel()
Dim objFSO As Object 'FileSystemObject
Dim databasefolderlocation As String
Dim objFolder As Object 'Folder
Dim objFile As Variant
Set objFSO = CreateObject("Scripting.FileSystemObject")
databasefolderlocation = Application.CurrentProject.Path 'This sets the folder, you can change it for another folder
Set objFolder = objFSO.GetFolder(databasefolderlocation)
For Each objFile In objFolder.Files
If objFile.Name Like "*.xls" Or objFile.Name Like "*.xlsx" Then
DoCmd.TransferSpreadsheet acLink, , objFile.Name, objFile.Path
End If
Next objFile
End Sub

VBA: Macro to copy cells that fall under condition to new tab

The code below will scan each column and copy the whole row that falls under the conditions (SEA, CUA, etc... and are red) to a sheet called "FileShares." (It is half way complete!!)
I would like two things done now, instead of copying the whole rows, I would like it to copy from source sheet (see example dataset1) the Target System (Application), UserID and Role Name to the destination sheet, "Fileshares" (see example dataset2) for each cells that matches the conditions. Only the bold headers will need to be filled. For the "Action" column, Remove needs to be placed into each row that has data.
Also, I would like to search columns dynamically up to the nth column (last column in the sheet) instead of hardcoding variable "k".
Any help, insight or suggestions would be greatly appreciated. Thanks!
Sub BulkUpload()
Dim rngCell As Range
Dim lngLstRow As Long
Dim keywords() As String, maxKeywords() As String
Dim totalKeywords As Integer, i&
Dim ws As Worksheet, resultsWS As Worksheet
Sheets.Add
ActiveSheet.Name = "FileShares"
Call Template
Set ws = Sheets("Sheet1")
Set resultsWS = Sheets("FileShares")
totalKeywords = 8
ReDim keywords(1 To totalKeywords)
ReDim maxKeywords(1 To totalKeywords)
maxKeywords(1) = "SEA"
maxKeywords(2) = "CUA"
maxKeywords(3) = "CCA"
maxKeywords(4) = "CAA"
maxKeywords(5) = "AdA"
maxKeywords(6) = "X"
maxKeywords(7) = "CUA" & Chr(10) & "SEA"
maxKeywords(8) = "CCA" & Chr(10) & "CUA" & Chr(10) & "SEA"
lngLstRow = ws.UsedRange.Rows.Count
Worksheets("FileShares").Select
j = 6
p = 1
q = 6
Dim k& ' create a Long to use as Column numbers for the loop
For k = 9 To 50
With ws
For Each rngCell In .Range(.Cells(8, k), .Cells(lngLstRow, k))
For i = LBound(maxKeywords) To UBound(maxKeywords)
If rngCell.Value = maxKeywords(i) And rngCell.Interior.ColorIndex = 3 Then
resultsWS.Cells(1000, k).End(xlUp).Offset(j + p, 0).EntireRow.Value = rngCell.EntireRow.Value
j = q + p - 7 'Used to start at row 8 and every row after
End If
Next i
Next rngCell
End With
Next k
End Sub
Excel can help you write your macros!
Using the macro recorder, perform the action manually. The recorder will transform your clicks and button presses into VBA.
Once recorded, step through your code. VBA's IDE includes a great feature. Pressing F8 allows you to advance the code one line at a time. This will help you figure out what each part of the recorded macro does. Top tip: Split your screen, so you can see both Excel and the VBA window. This will help get to grips with the code, as you see the impact each line has on the UI.
When you find a line of code you do not understand refer to Microsoft's documentation. Most items include a working example, as well as an explanation.
You can learn so much following this technique. But there is a limit. If you cannot perform the action manually, you cannot record it. To take your skills to the next level I would recommend (in order):
Attend a training course.
Read a book.
Read blogs.
Although the internet contains a wealth of information most of it is in bite sized chunks. Courses and books cover the fundamentals in detail, giving you the tools you'll need to solve these problems.
I like books published by Wrox. But I would recommend you preview a page or two before buying. I hate reading books written in a style I do not get on with.

Text File to Array in Access VBA

I am looking to load a .txt file into a VBA array (in an access VBA), manipulate it there, and then paste the manipulated array into an access table. I will loop this macro then through round about 500 .txt files and populate the database with years of data.
I have done it before using Excel in this way:Populating the sheet for 1 .txt file, manipulating the data, loading into the database, clearing the sheet, and loading the next txt file and loop till all files have been processed. However, this takes years and it becomes obvious that Excel is not really needed, since everything is stored in Access anyway.
Since then, I have tried to figure out how to do it in access straight away, without using excel, but I have been struggling. I found some nice code on this board here:
Load csv file into a VBA array rather than Excel Sheet
and tried to amend it so it would work for me. Especially The_Barman's Answer at the bottom seems simple and very interesting.
I am new to arrays, VBA, SQL, basically everything, so maybe there is some big mistake I am making here that is easy to resolve. See my code below:
Sub LoadCSVtoArray()
Dim strfile As String
Dim strcon As String
Dim cn As ADODB.Connection
Dim rs As Recordset
Dim rsARR() As Variant
Set cn = New ADODB.Connection
strcon = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" & "\\filename.txt" &
";Extended Properties=""text;HDR=Yes;FMT=Delimited"";"
cn.Open strcon
strSQL = "SELECT * filename.txt;"
Set rs = cn.Execute(strSQL)
rsARR = WorksheetFunction.Transpose(rs.GetRows)
rs.Close
Set cn = Nothing
[a1].Resize(UBound(rsARR), UBound(Application.Transpose(rsARR))) = rsARR
End Sub
I dont even know if the bottom part of the code works, because an error message pops up that the file is not found. The interesting thing is, if I debug and copy the value of strcon into windows "run", it opens the correct file. So I guess the path is correct? Can I even open a .txt file through an ADODB connection? Right now I am a bit confused if this is possible and if it is the best solution.
Some more background regarding the text files I am trying to save into the array:
-They are output from another program, so it is always the same structure and very oreganized
it comes in this format:
Column a Column b ....
data 1 data 1
data 2 Data 2
...
and so on.
If possible, I would like to retain this structure, and even safe it as a table with the first row as column headers.
The Data Source is the folder path containing the file and the file is the table (SELECT * FROM ..). Replace "\\filename.txt" in strcon with the folder path.
http://www.connectionstrings.com/textfile/

Combining two Microsoft Access Databases into one with a form to choose between them

So I've been assigned to work on two identical databases which are very poorly made. Both are split up into front/back ends, but that have different data.
Is it possible to combine both databases into one and create a form to allow the user to choose which "database" they will interact with?
To clarify, the school that utilizes these databases has a database for the Fall semester and Spring semester. Both contain the exact same forms/queries/tables, but the specific data differs. e.g. Maybe a teacher teaches 3 classes in the fall, but 4 classes in the Spring (the database is way more complicated that this though). The structure of the data remains the same, but the specific data changes depending on the database.
So, if they need to access data, they need to open the corresponding database. As a result, they are constantly open/closing different databases. What they want to do is be able to just use one database and switch between Fall/Spring.
Is this at all possible?
Without knowing the exact structure of your data it's hard to give specific answers, but my gut feeling is most definitely these two databases could be combined. Combine all the data in tables of the same name, but add a "Semester" column to any table that would result in duplicated data. Then simply create the application frontend so wherever necessary the user would select the Semester they want to view, and pass it to your queries retrieving the data. You could even carry the "Semester" value from the beginning as you point out, and pass it from screen to screen (w/ a global application-level variable).
It's been a while since I've had to code in Access in my professional life, but I'm sure there's a way to accomplish your goal. Maybe post more details of your specific data structure, and we can all jump in and give pointers!
Here is a table linking function that I've used many times over the years. In order to use this, you'll have to provide a couple of functions:
ThisTableShouldBeChanged is passed the name of a table and returns true if the link to the table should be re-linked as part of the database switch. This will allow you to selectively exclude tables from the switch.
GetConnectionString should return the name of the new connection string. Constructing a connection string to another Access database is simple, just put the full database path into this format: ";DATABASE=path"
In order to do what you want, you just have to run this function when the user chooses a new databse to open. One word of advice: I would put an unmistakable element in the UI, or example the background color of the screens, that will signal to the user which database they are working on so there is no confusion.
This function can also be used to link tables to an ODBC or SQL Server connection when deploying into a production environment. That's what I generally use it for.
Public Function LinkTables() As Boolean
On Error GoTo HandleError
Dim tdf As TableDef
Dim tdfNew As TableDef
Dim strName As String
Dim tdfs As TableDefs
Dim strSource As String
Dim strConnect As String
Dim blnThrowErr As Boolean
Dim arr() As Variant
Set tdfs = CurrentDb.TableDefs
Dim intCount As Integer
Dim i As Integer
intCount = tdfs.Count
strConnect = GetConnectionString()
i = 1
'Save a copy of the existing table names'
ReDim arr(tdfs.Count, 2)
For Each tdf In tdfs
strName = tdf.Name
If ThisTableShouldBeChanged(strName) Then
If tdf.Connect <> "" Then
strSource = tdf.SourceTableName
arr(i, 1) = strName
arr(i, 2) = strSource
i = i + 1
End If
End If
Next tdf
tdfs.Refresh
Dim strNameRep As String
'Create new linked tables'
For i = 1 To UBound(arr)
If arr(i, 1) <> "" Then
Set tdfNew = New TableDef
strName = arr(i, 1)
tdfNew.Name = arr(i, 1)
tdfNew.SourceTableName = arr(i, 2)
tdfNew.Connect = strConnect
strNameRep = strName & "_temp"
'rename the old table'
tdfs(strName).Name = strNameRep
tdfs.Refresh
tdfs.Append tdfNew
If Err.Number <> 0 Then
Debug.Print Err.Description
tdfs(strNameRep).Name = strName
Err.Clear
Else
tdfs.Delete strNameRep
End If
On Error GoTo HandleError
End If
Next i
tdfs.Refresh
LinkTables = True
ExitHere:
Exit Function
HandleError:
MsgBox Err.Description & " (" & Err.Number & ")"
LinkTables = False
Resume ExitHere
End Function
I think it should be possible to create queries for each of the tables with the original table name and name the new, combined tables with an extra letter or two. Let us say you have a table Students, the new table becomes JointStudents and you create a query Students:
SELECT F1, F2, F3 FROM JointStudents WHERE Semester=Forms!PickSem!txtSemester
This means that any form or query that refers to the table Students will now pick up the query students with the built in filter.
A least-initial-cost solution, setting up an interface to link to the appropriate semester, is still going to involve touching all the forms and adding another (for user-friendly control of the "active" db), and you'll still be incurring additional cost for any changes needed down the road (as they'll be made in two places rather than one).
You don't have to change the tables by hand: you can write SQL to do it. It's possible to write a procedure that steps through existing tables and adds a "Semester" column and populates it ... best to run it in both databases so that you can also populate the columns in the same procedure. If the tables are named the same in both databases, it would even be possible to run a procedure that would step through all of them, updating Fall, updating Spring, and appending Spring to Fall so that you have everything in one database.
A safer method would probably be to copy the Fall structure to a new table and modify that table, in case something happens and you need to "roll back" what you've done.
Given that this question was asked months ago and you're working with a school, you've probably either resolved the issue somehow or have had to put it aside, but if there's still a need for it, I can post VBA that should help.

Resources