【问题标题】:Excel ADO in ASP Classic - Return row 2 as headerASP Classic 中的 Excel ADO - 返回第 2 行作为标题
【发布时间】:2013-09-04 21:53:03
【问题描述】:

如何在通过 ADODB.Connection 读取 Excel(xls 和 xlsx)中的第二行作为列标题???

  • Excel 来自客户
  • 在上传到 ASP 页面之前不能更改 Excel
  • 第一行是 Excel 报告的 TITLE(我想忽略)

当前解决方案(代码在最后):

  1. 客户端上传 Excel
  2. 将备份副本创建为“原始”
  3. 打开并读取 Excel 的前 2 行
  4. 用第 2 行的值覆盖第 1 行
  5. 前 2 行现在相同
  6. 关闭并重新打开 Excel
  7. 选择除与列名相同的值之外的所有内容 -例如从 [Sheet1$] 中选择 column1、column2、column3... 其中 ucase(column1) 'COLUMN1'
  8. 读取 Excel 并打印 HTML

我想不通的可能想法:

  • 将“命名范围”分配给第二行
  • 删除第一行(用 ADO 和 Excel 似乎不可能)
  • 在 VBA 中使用 HeaderRowRange.Address 将标题行更改为第 2 行
  • 不要在当前解决方案中创建副本,而是读取第 2 行及以下的所有内容,然后保存到新的 Excel 中

当前解决方案的代码:

' ---------------------------------------------------------------------
' 1. Client uploads Excel
' ---------------------------------------------------------------------
xlsfolder = "D:\website\excels\"
filename = request("filename_from_asp_upload_form")
xlsfile = xlsfolder & filename
' ---------------------------------------------------------------------
' 2. Create backup copy as "original"
' ---------------------------------------------------------------------
dim fscopy
set fscopy=Server.CreateObject("Scripting.FileSystemObject")
exceltypelower = fscopy.GetExtensionName(xlsfile)
replace_text = "_BACKUP." & exceltypelower
xlsfile_copy = replace(xlsfile, ("." & exceltypelower), replace_text)
fscopy.CopyFile xlsfile,xlsfile_copy
set fscopy=nothing
' ---------------------------------------------------------------------
' EXTRA STEP - SELECT EXCEL DRIVER
' ---------------------------------------------------------------------
if  exceltype = "XLS" then
    exceldriver = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & xlsfile & ";Extended Properties=""Excel 8.0;"""
elseif exceltype = "XLSX" then
    exceldriver = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & xlsfile & ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"""
    exceldriver2 = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & xlsfile & ";Extended Properties=""Excel 8.0;HDR=No;"""
    '*** Second driver allows first row to be read as data and not header allowing Excel cells to be overwritten
else
    '**** default to 97-2003 format. ELSE just in case
    exceldriver = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & xlsfile & ";Extended Properties=""Excel 8.0;"""
end if
' ---------------------------------------------------------------------
' 3. Open and Read first 2 rows of Excel
' 4. Overwrite row 1 with values from row 2
' ---------------------------------------------------------------------
' NOTE: There is another step at UPLOAD that the user flags what kind of Excel Report it is. 
'   Since there are just a few now, I have the skip_title variable hard-coded
'   IF SKIP_TITLE = "Y"... that means the column headers are on row 2
'   IF SKIP_TITLE = "N"... that means the column headers are on row 1 and this is unnecessary 
' ---------------------------------------------------------------------
if skip_title = "Y" then
    excel_query2 = "Select * from [Sheet1$] "
    Const adOpenStatic = 3
    Const adLockOptimistic = 3
    Const adUseClient = 3
    Set objConn = CreateObject("ADODB.Connection")
    Set objRecordset = CreateObject("ADODB.Recordset")
    objConn.Open (exceldriver2)
    objRecordset.CursorLocation = adUseClient
    objRecordset.Open excel_query2 , objConn, adOpenStatic, adLockOptimistic

    counter = 1
    headcount = 0 
    colcount = objRecordSet.Fields.Count    ' How many columns there are to traverse
    p = objRecordSet.GetRows(colcount,0)    ' Write excel_query2 results for number of columns above

    objRecordSet.MoveFirst
    do until objRecordSet.EOF  or counter = 0 
        for each x in objRecordSet.Fields
            objRecordSet.Fields(headcount).Value = p(headcount, 1)  'overwrite row 1 with row 2
            headcount = headcount + 1
            next
        counter = counter - 1
        objRecordSet.MoveNext
    loop
    objRecordSet.Update
    objRecordset.Close
    objConn.Close
end if 
' ---------------------------------------------------------------------
' 5. The first 2 rows are now identical
' 6. Close and Re-Open Excel
' ---------------------------------------------------------------------
set objConnection = Server.CreateObject("ADODB.Connection") 
objConnection.Open (exceldriver)
' ---------------------------------------------------------------------
' 7. Select everything except values that equal name of column names
' -e.g. Select column1, column2, column3... from [Sheet1$] where ucase(column1) <> 'COLUMN1'
' ---------------------------------------------------------------------
excel_query = "Select column1, column2, column3... from [Sheet1$] where ucase(column1) <> 'COLUMN1' " 'example
set objRS = objConnection.execute(excel_query)
' ---------------------------------------------------------------------
' 8. Read Excel and Print HTML
' ---------------------------------------------------------------------
response.write "<table><thead><tr>"
for each x in objRS.Fields
    response.write("<th>" & ucase(x.name) & "</th>") 
next
response.write "</tr></thead>"

response.write "<tbody>"
do until objRS.EOF
    response.write "<tr>"   
    for each fld in objRS.Fields
        response.write "<td>" & fld.value & "</td>" 
    next
    response.write "</tr>"  
    objRS.MoveNext
loop
objRS.close
objConnection.Close
response.write "</tbody></table>"
' ---------------------------------------------------------------------
' Le Fin or Da End

【问题讨论】:

  • 如何从工作表中运行select count(*) 来确定行数,然后select * ... where false 来获取列数。一旦你有了这些,你就可以确定你需要读取的范围,并明确地使用它(例如)select from [Sheet1$A2:E500]
  • 查找范围不是问题。最终,它可以像使用第 1 行一样将第 2 行用作列标题。假设第 2 行的第 3 列是“STATE”,我希望能够执行 select * from [Sheet1$] where state='Florida' order by state 而不必执行 select * from [Sheet1$] where F3='Florida' order by F3 .因此,为了避免将第 2 行的值写入第 1 行。

标签: excel vba asp-classic ado


【解决方案1】:
Sub Test22()

Dim cn As New ADODB.Connection
Dim rsT As New ADODB.Recordset
Dim numCols As Long, numRows As Long, x As Long

    With cn
        .Provider = "Microsoft.Jet.OLEDB.4.0"
        .ConnectionString = "Data Source=" & ThisWorkbook.Path & _
                            "\ADOXSource.xls;Extended Properties=Excel 8.0;"
        .CursorLocation = adUseClient
        .Open
    End With

    'read the whole sheet
    rsT.Open "select * from [Sheet1$]", cn
    rsT.MoveLast

    'get # of rows/cols
    numCols = rsT.Fields.Count
    numRows = rsT.RecordCount

    rsT.Close

    'get data from specific range
    rsT.Open "select * from [Sheet1$A2:" & Chr(64 + numCols) & (numRows - 1) & "]", cn

    'I'm testing this in excel...
    For x = 0 To rsT.Fields.Count - 1
        Sheet1.Range("a1").Offset(0, x).Value = rsT.Fields(x).Name
    Next x
    Sheet1.Range("a2").CopyFromRecordset rsT

End Sub

【讨论】:

  • 谢谢!在打印数据之前,仍然需要一些生成的 SQL INSERTS 的列名。因此,需要将第 1 行(而不是 A2)​​写入数组或串联字符串。要首先获得第 1 行,我会将 FOR 循环更改为 For x = rsT.Fields.Count - 1 To x=0 吗?另外,如果我将它们写入数组或连接字符串,你能想出一种方法来保证列名的顺序与数据的顺序相匹配吗?最后,你的连接字符串和我的有很大区别吗?
  • 字段名称不在任何行中 - 它们存储在记录集的 Fields 集合中。我的 For 循环遍历列,而不是行。
  • 对,通过列名,我意识到这是Fields VALUE,而不是Fields NAME。我的意思是问是否可以保留 VALUE 以便 SQL INSERTS 但我想我想出了那部分......在For Loop 中继续用逗号附加名称sql_cols = sql_cols &amp; "," &amp; Field(#).Value
  • 另一方面,FOR 循环看起来像是将列 .NAMEs(F1、F2 等)从第 2 行写入第 1 行作为单元格 .VALUEs (不是 .NAMEs ),对吗?这似乎只是重复了前两行,不是吗?有没有办法改变列或至少第 1 行的 .NAME 属性?我认为这会解决问题。
  • 该代码只是您可以采取的一种方法的说明 - 它并不是针对您的特定问题的完整解决方案。正如发布的那样,它只是在工作表上显示记录集字段和内容,以确认查询的范围正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2016-04-25
  • 1970-01-01
  • 2017-10-30
  • 2014-04-06
  • 1970-01-01
相关资源
最近更新 更多