{"id":54363,"date":"2009-02-18T21:15:00","date_gmt":"2009-02-18T21:15:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/02\/18\/hey-scripting-guy-how-can-i-tell-which-tables-and-columns-are-in-a-database-without-opening-it\/"},"modified":"2009-02-18T21:15:00","modified_gmt":"2009-02-18T21:15:00","slug":"hey-scripting-guy-how-can-i-tell-which-tables-and-columns-are-in-a-database-without-opening-it","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-tell-which-tables-and-columns-are-in-a-database-without-opening-it\/","title":{"rendered":"Hey, Scripting Guy! How Can I Tell Which Tables and Columns Are in a Database Without Opening It?"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"> <\/h2>\n<p>Hey, Scripting Guy! Is there a way to figure out which tables and columns are in a database without having to open it up?<\/p>\n<p>&#8211; DT<\/p>\n<p><img decoding=\"async\" height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\"><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"><\/p>\n<p>Hi DT,<\/p>\n<p>The Impressionists are back in Chicago! For the past six months, they were hibernating down in Ft. Worth, Texas, in the United States. But now they are back. In true Ferris Bueller style, I am standing here looking at Cezanne\u2019s <a href=\"http:\/\/www.artic.edu\/aic\/collections\/artwork\/14561\" target=\"_blank\">The Vase of Tulips<\/a> and am <a href=\"http:\/\/en.wikipedia.org\/wiki\/Impressionism\" target=\"_blank\">impressed<\/a> by the way the flowers seem at ease with their surroundings, are at one with their vase, and enjoy a brief repose with the fruit on the table (of course the fruit is innocent\u2014it was <a href=\"http:\/\/dictionary.reference.com\/browse\/framed\" target=\"_blank\">framed<\/a>; we\u2019ll be here all week, try the veal). It is the structure of the painting that creates an organic entity from myriad brush strokes. It is the schema that impresses me. <\/p>\n<p>To become one with your database, you will also want to enjoy the schema of the database. While waiting for a pizza, we knocked out the <b>GetDataBaseSchema.ps1<\/b> script.<\/p>\n<table class=\"dataTable\" id=\"EJD\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\">This week we will be talking about scripting Microsoft Office Access. The <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/db.mspx\" target=\"_blank\">Databases hub<\/a> is a great jumping-off point because it highlights the available Office Access scripts we have in the TechNet Script Center. There are also links to the Office Access scripts we have in the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/misc\/database\/default.mspx?mfr=true\" target=\"_blank\">Script Center Script Repository<\/a> and on the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/csc\/scripts\/databases\/default.mspx\" target=\"_blank\">Community-Submitted Scripts Center<\/a>. When taken as a whole, there are tons of examples for you to review. For information about downloading and installing Windows PowerShell, you can go to the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx\" target=\"_blank\">Windows PowerShell Scripting Hub<\/a>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>The The <b>GetDataBaseSchema.ps1<\/b> script is seen here.<\/p>\n<pre class=\"codeSample\">Function Check-Path($Db)\n{\n If(!(Test-Path -path (Split-Path -path $Db -parent)))\n   {\n     Throw \"$(Split-Path -path $Db -parent) Does not Exist\"\n   }\n  ELSE\n  {\n   If (!(Test-Path -Path $Db))\n     {\n      Throw \"$db does not exist\"\n     }\n  }\n} #End Check-Path\nFunction New-Line (\n                  $strIN,\n                  $char = \"=\",\n                  $sColor = \"Yellow\",\n                  $uColor = \"darkYellow\",\n                  [switch]$help\n                 )\n{\n if($help)\n  {\n    $local:helpText = `\n@\"\n     New-Line accepts inputs: -strIN for input string and -char for seperator\n     -sColor for the string color, and -uColor for the underline color. Only\n     the -strIn is required. The others have the following default values:\n     -char: =, -sColor: Yellow, -uColor: darkYellow\n     Example:\n     New-Line -strIN \"Hello world\"\n     New-Line -strIn \"Morgen welt\" -char \"-\" -sColor \"blue\" -uColor \"yellow\"\n     New-Line -help\n\"@\n   $local:helpText\n   break\n  } #end New-Line help\n $strLine= $char * $strIn.length\n Write-Host -ForegroundColor $sColor $strIN\n Write-Host -ForegroundColor $uColor $strLine\n} #end New-Line function\nFunction Get-DataType ($enum)\n{\n [enum]::Parse(\"system.data.oledb.oledbtype\",$enum)\n} #end Get-DataType\nFunction Get-DataBaseSchema ($Db)\n{\n New-Line(\"Obtaining Schema for $Db\")\n $Provider = \"Provider=Microsoft.Jet.OLEDB.4.0\"\n $DataSource = \"Data Source =\"+$Db\n $Connection = New-Object Data.OleDb.OleDbConnection(\"$Provider;$DataSource\")\n $Connection.open()\n $Tables = $Connection.GetSchema(\"Tables\") |\n   ForEach-Object {\n      if($_.Table_Type -eq \"TABLE\")\n        { $_.Table_Name }\n     }\n   ForEach($table in $Tables)\n    {\n     New-Line(\"Table Name: \" + $table)\n     $SchemaColumn = $Connection.GetSchema(\"Columns\")\n     $schemaColumn |\n     foreach-Object {\n        If($_.Table_Name -eq $Table)\n         { \"$($_.COLUMN_Name) $(Get-DataType($_.DATA_TYPE))\" }\n      } #end Foreach-Object\n    } #end Foreach table\n$Connection.Close()\n}\n# *** Entry Point to Script ***\n$Db = \"C:\\FSO\\ComputerData.mdb\"\nCheck-Path($Db)\nGet-DataBaseSchema($Db)\n<\/pre>\n<p>Ok, so the <b>GetDataBaseShema.ps1<\/b> script is rather long. But we have already talked about several of the functions recently:<\/p>\n<table class=\"\" cellSpacing=\"0\" cellPadding=\"0\" border=\"0\">\n<tbody>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p><a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/feb09\/hey0216.mspx\" target=\"_blank\"><b>Check-Path function<\/b><\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p><a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/feb09\/hey0209.mspx\"><b>New-Line function<\/b><\/a><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <b>Get-DataType<\/b> function accepts a single parameter\u2014a number that represents a data type enumeration value. Inside the function, it uses the <b>Parse<\/b> static method from the <b>System.Enum<\/b> .NET Framework class. The <b>Parse<\/b> method takes two parameters: the first is the name of the enumeration class, and the second is the value of the enumeration. If we were to call this from within the Windows PowerShell console, it might look like this:<\/p>\n<pre class=\"codeSample\">PS C:\\&gt; [enum]::Parse(\"system.data.oledb.oledbtype\",3)\nInteger\n<\/pre>\n<p>As you can see, it takes a number and turns it into a string, so the output is more intelligible. We will use this when we display the data types contained in each column of the database table. The function is seen here:<\/p>\n<pre class=\"codeSample\">Function Get-DataType ($enum)\n{\n [enum]::Parse(\"system.data.oledb.oledbtype\",$enum)\n} #end Get-DataType\n<\/pre>\n<p>Now we come to the main function we will be creating today. The <b>Get-DataBaseSchema<\/b> function accepts a path to the database file as seen here:<\/p>\n<pre class=\"codeSample\">Function Get-DataBaseSchema ($Db)\n<\/pre>\n<p>We then call the <b>New-Line<\/b> function to print out the name of the database with an underline:<\/p>\n<pre class=\"codeSample\">New-Line(\"Obtaining Schema for $Db\")\n<\/pre>\n<p>Now we specify the provider and the data source, create the connection to the database, and open the connection. This is seen here:<\/p>\n<pre class=\"codeSample\">$Provider = \"Provider=Microsoft.Jet.OLEDB.4.0\"\n $DataSource = \"Data Source =\"+$Db\n $Connection = New-Object Data.OleDb.OleDbConnection(\"$Provider;$DataSource\")\n $Connection.open()\n<\/pre>\n<p>We retrieve the database schema by using the <b>GetSchema<\/b> method. We are interested in tables, and therefore limit the returned information to tables by supplying the &#8220;Tables&#8221; keyword to the <b>method<\/b> call. We then take the collection of table schema objects and pipeline it to the <b>ForEach-Object<\/b> cmdlet, which allows us to work through the collection of objects. We use the <b>$_<\/b> variable to refer to a single object on the pipeline. We look at the <b>table_type<\/b> property. If it is equal to <b>TABLE<\/b>, we know it was a user-created table and not a system table. We then sent the table name back to the <b>$Tables<\/b> variable. This section of code is seen here:<\/p>\n<pre class=\"codeSample\">$Tables = $Connection.GetSchema(\"Tables\") |\n   ForEach-Object {\n      if($_.Table_Type -eq \"TABLE\")\n        { $_.Table_Name }\n     }\n<\/pre>\n<p>Well, that was as much fun as extra mushrooms and sausage on a Chicago deep-dish pizza. Because it is possible the database could have more than user-created table in the database, we use the <b>ForEach<\/b> statement to walk through the array of table names stored in the <b>$Tables<\/b> variable. If there is only one table, we do not need to do anything because Windows PowerShell does not generate an error the way VBScript would. We then print out the name of the table and underline it by using the <b>New-Line<\/b> function. This is seen here:<\/p>\n<pre class=\"codeSample\">ForEach($table in $Tables)\n    {\n     New-Line(\"Table Name: \" + $table)\n<\/pre>\n<p>The next thing we need to do is call the <b>GetSchema<\/b> method and tell it we are interested in the columns. We then pipeline the columns to the <b>ForEach-Object<\/b> cmdlet and look for columns that have the same table name that is stored in the <b>$table<\/b> variable. After we have retrieved the columns for the particular table, we print out the column name and send the <b>data_type<\/b> value to the <b>Get-DataType<\/b> function. We talked about this earlier. This portion of the function is seen here: <\/p>\n<pre class=\"codeSample\">     $SchemaColumn = $Connection.GetSchema(\"Columns\")\n     $schemaColumn |\n     foreach-Object {\n        If($_.Table_Name -eq $Table)\n         { \"$($_.COLUMN_Name) $(Get-DataType($_.DATA_TYPE))\" }\n      } #end Foreach-Object\n    } #end Foreach table\n<\/pre>\n<p>The last thing we need to do is close the connection to the database. This is important to free resources and to minimize database locking issues. To close the database, we use the <b>Close<\/b> method from the <b>connection<\/b> object as seen here:<\/p>\n<pre class=\"codeSample\">$Connection.Close()\n<\/pre>\n<p>The entry point to the script takes the path to the database, and then calls the <b>Check-Path<\/b> and <b>Get-DataBaseSchema<\/b> functions as seen here:<\/p>\n<pre class=\"codeSample\">$Db = \"C:\\FSO\\ComputerData.mdb\"\nCheck-Path($Db)\nGet-DataBaseSchema($Db)\n<\/pre>\n<p>When we run the script, we are given the output seen here:<\/p>\n<p><img decoding=\"async\" height=\"352\" alt=\"Image of the script's output\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/february\/hey0218\/hsg_2_18_09-01.jpg\" width=\"500\" border=\"0\"><\/p>\n<p>&nbsp;<\/p>\n<p>Well, DT, we have come to the conclusion of another script. I predict you will be able to use the ability to write to an Access Database often. We will see you tomorrow for another really cool installment of \u201cHey, Scripting Guy!\u201d as Office Access Database Week continues. Until then, keep on scripting (thanks CA)!<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! Is there a way to figure out which tables and columns are in a database without having to open it up? &#8211; DT Hi DT, The Impressionists are back in Chicago! For the past six months, they were hibernating down in Ft. Worth, Texas, in the United States. But now they are [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[146,54,49,3,45],"class_list":["post-54363","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-databases","tag-microsoft-access","tag-office","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! Is there a way to figure out which tables and columns are in a database without having to open it up? &#8211; DT Hi DT, The Impressionists are back in Chicago! For the past six months, they were hibernating down in Ft. Worth, Texas, in the United States. But now they are [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54363"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54363\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54363"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54363"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}