3

我从用户那里获取输入,我给他选择。例如,在“导体”列中,他可以选择“Al 或 Cu”

同样,在“绝缘体”列中,他可以选择“XLPE 或 PVC”

现在以上两个是我提供给用户的列表的简单版本,但是我的一些列表依赖于以前的输入,为此我使用这样的数据验证:

=indirect($C5 & "_" & $D5) 

(命名范围,例如:al_xlpe)

假设 C 列和 D 列引用了一些输入(这将导致 2! 命名范围在前面定义)。

由于上述方法,我被迫使用大量命名范围(我的一些验证列表选择取决于超过 4 个或更多输入,它们如下所示:

=indirect("col6" & "_" & col7 & "_" & col8 & "_" & col9) 

(另一个命名范围,例如:al_xlpe_duct_3;可能有 4 个!其中)

我面临几个问题:

  1. 由于数据库可以随时扩展,因此包含超过 4 个输入的验证列表将需要 4 个!命名范围更改。
  2. 数据验证容易丢失(一大问题)
  3. 我不能限制复制和粘贴,因为我的大多数用户将粘贴来自其他工作表的数据(不能使用导入,因为列永远不会被修复)
  4. 我不能使用列表框,因为可以输入任意数量的数据行,并且我需要在每一行中进行选择
  5. 无法使用 MS Access 进行数据库管理,因为我的工具适用于输入数据,而且我的大多数用户都不熟悉访问(而且它不允许轻松复制粘贴数据)

有没有更好的方法?

4

2 回答 2

1

在这个答案中,我提供了一种在我 45 年开始作为程序员工作时非常流行的技术,但多年来我没有看到任何人在常规应用程序中使用过这种技术,除了我自己。我们从编译器开发中借用了该技术,但使用它的正式程度要低得多。

在成熟的技术中,将有五个步骤:

  1. 设计一种以方便人类的方式对规范进行编码的方法。
  2. 编码规范。
  3. 设计一个或多个表以方便快速处理的方式保存规范。
  4. 设计并实现将人类格式转换为快速处理格式的程序。
  5. 设计和实现一个程序来解释快速处理格式并执行所需的操作。

并非每个问题都需要所有五个步骤。有时人工和快速处理格式可能是相同的。这听起来可能很复杂,但它使我们能够轻松有效地解决许多复杂的问题。

在下面的工作表中,我已经编码了我对您需要的验证类型的理解。

  |         A          |         B          |         C          |
--+--------------------+--------------------+--------------------+    
 1|Permitted           |Conditions -------------->               |
 2|C=V1|V2|V3|V4       |                    |                    |
 3|D=V5                |C=V1|V2             |                    |
 4|D=V6                |C=V3|V4             |                    |
 5|E=V7|V8             |D=V5                |C=V1                |
 6|E=V9|V10            |D=V5                |C=V2                |
 7|E=V11|V12           |D=V6                |C=V3                |
 8|E=V13|V14           |D=V6                |C=V4                |

在第 2 行中,我声明 C 列中的一个单元格的值可以是 V1 或 V2 或 V3 或 V4。

在第 3 行中,我声明 D 列中的一个单元格的值可能为 V5,但前提是同一行的 C 列的值为 V1 或 V2。

在第 4 行中,我为 D 列中的一个单元格声明了一个替代值,它具有自己的一组条件。

在第 5 行中,我声明 E 列中的一个单元格的值可能为 V7 或 V8,但前提是同一行的 D 列的值为 V5,且该行的 C 列的值为 V1。

我对您的要求没有足够的了解,无法知道这是否是您验证要求的最佳或完整表示。但是,如果您喜欢这种技术,我希望您能理解并为您的需求开发一个方便的表示。

接下来我需要定义这个规范的快速处理形式。我设计了四个表格并实现了下面的代码,将人工工作表格式转换为快速处理格式,然后将这些表格的内容输出到准备放置在此答案中的即时窗口:

Rules per Column table 
C RR RR                = Column   First rule   Last rule
3  1  1
4  2  3
5  4  7

工作表中有三列有验证规则,它们是列 3 (C)、列 4 (D) 和列 5 (E)。上表告诉我们,对于第 3 (C) 栏,适用规则 1 至 1,对于第 5 (E) 栏,适用规则 4 至 7。

Rule table              
I VV VV CC CC   = Index   First value   Last value   First condition   Last condition
1  1  4  1  0 
2  5  5  1  1 
3  8  8  2  2 
4 11 12  3  4 
5 15 16  5  6 
6 19 20  7  8 
7 23 24  9 10 

对于规则 1,条件 1 到 0 适用,即没有条件。允许的值是值表中的条目 1 到 4(V1、V2、V3 和 V4)。这对应于工作表中的第 2 行。

对于规则 4,允许的值是值表中的条目 11 和 12(V7 和 V8),提供条件 3 到 4 适用。条件 3 是列 4 (D) 必须等于值表中的条目 13 (V5)。条件 4 是列 3 (C) 必须等于值表中的条目 14 (V1)。这对应于工作表中的第 5 行。

Condition table
 I C VV VV          = Index   Column   First value   Last value
 1 3  6  7
 2 3  9 10
 3 4 13 13
 4 3 14 14
 5 4 17 17
 6 3 18 18
 7 4 21 21
 8 3 22 22
 9 4 25 25
10 3 26 26

Value table                 Entries 1 to 26
E 1=V1  E 2=V2  E 3=V3  E 4=V4  E 5=V5  E 6=V1  E 7=V2  E 8=V6  E 9=V3  E10=V4   
E11=V7  E12=V8  E13=V5  E14=V1  E15=V9  E16=V10 E17=V5  E18=V2  E19=V11 E20=V12  
E21=V6  E22=V3  E23=V13 E24=V14 E25=V6  E26=V4

如果您不习惯考虑通过链接表控制代码,则可能需要一点时间才能完全理解。我遵循了一些规则的链接。多试几次,你就会明白了。请注意工作表是如何设计为易于人类维护的,而这些表是为计算机快速执行而设计的。

此编译过程可以在 Worksheet Open 例程中进行,或者您可以预编译表并将其存储在工作簿中。这些表已准备好由工作表更改例程执行,或者它们可用于计算公式并将其放置在适当的单元格中。

我希望我已经很好地解释了这一点,以便您了解这个想法并确定该技术是否适合您的问题。如有必要,可以提出问题,我将扩大解释。

以下代码将人类格式转换为快速处理格式,然后将快速处理格式输出到即时窗口。

Option Explicit

  Type typColRule      ' Definition of entry in Rules per Column table 
    InxRule1 As Long   ' Index of first rule for this column. ) InxRule1 > InxRuleL 
    InxRuleL As Long   ' Index of last rule for this column.  ) if no rules for column
  End Type
  Type typRule         ' Definition of Rule table 
    InxValue1 As Long  ' Index of first permitted value for this rule
    InxValueL As Long  ' Index of last permitted value for this rule
    InxCond1 As Long   ' Index of first condition for this column. ) InxCond1 > InxCondL 
    InxCondL As Long   ' Index of last rule for this column.       ) if no rules for column
  End Type
  Type typCond         ' Definition of Condition table
    Col As Long        ' Column to which this condition applies
    InxValue1 As Long  ' Index of first permitted value for this condition
    InxValueL As Long  ' Index of last permitted value for this condition
  End Type

  ' ColRule is sized to (Min to Max) where Min is the lowest column validated
  ' and Max is the highest column validated.  ColRule(N).InxRule1 identifies
  ' the first rule in Rule for column N.  ColRule(N).InsRuleL identifies the
  ' last rule in Rule for column N.
  Dim ColRule() As typColRule

  ' There is one entry in Rule per validation row in worksheet "Validate".
  Dim Rule() As typRule

  ' There is one entry in ValueCell per value referenced in a permitted or
  ' a condition.
  Dim ValueCell() As String

  ' There is one entry in Cond per condition in worksheet "Validate"
  Dim Cond() As typCond

Sub CompileValidation()

  Dim ColCodeCrnt As String
  Dim ColNumCrnt As String
  Dim ColValCrnt As Long
  Dim ColValidateCrnt As Long
  Dim ColValMin As Long
  Dim ColValMax As Long
  Dim ConditionCrnt As String
  Dim InxCondCrnt As Long
  Dim InxRuleCrnt As Long
  Dim InxValueCellCrnt As Long
  Dim InxValueListCrnt As Long
  Dim NumCond As Long
  Dim NumValue As Long
  Dim PermittedCrnt As String
  Dim PosEqual As Long
  Dim RowValidateCrnt As Long
  Dim ValueList() As String

  With Worksheets("Validate")

    ' Determine the size of the arrays to which information will be
    ' compiled.  Find
    '   * The minimum and maximum columns subject to validated
    '   * Number of conditions
    '   * Number of values references
    ' This routine does not allow for blank rows or columns in the
    ' middle of worksheet "Validate".
    ColValMin = -1
    ColValMax = -1
    NumCond = 0
    NumValue = 0
    RowValidateCrnt = 2
    Do While True
      PermittedCrnt = .Cells(RowValidateCrnt, 1).Value
      If PermittedCrnt = "" Then
        Exit Do
      End If
      PosEqual = InStr(1, PermittedCrnt, "=")
      Debug.Assert PosEqual > 1
      ' Determine range of columns validated
      ColCodeCrnt = Mid(PermittedCrnt, 1, PosEqual - 1)
      ColNumCrnt = Range(ColCodeCrnt & "1").Column
      If ColValMin = -1 Then
        ColValMin = ColNumCrnt
      ElseIf ColValMin > ColNumCrnt Then
        ColValMin = ColNumCrnt
      End If
      If ColValMax = -1 Then
        ColValMax = ColNumCrnt
      ElseIf ColValMax < ColNumCrnt Then
        ColValMax = ColNumCrnt
      End If
      ' Determine number of conditions and number of values
      ValueList = Split(Mid(PermittedCrnt, PosEqual + 1), "|")
      NumValue = NumValue + UBound(ValueList) - LBound(ValueList) + 1
      ColValidateCrnt = 2
      Do While True
        ConditionCrnt = .Cells(RowValidateCrnt, ColValidateCrnt).Value
        If ConditionCrnt = "" Then
          Exit Do
        End If
        PosEqual = InStr(1, ConditionCrnt, "=")
        Debug.Assert PosEqual > 1
        ValueList = Split(Mid(ConditionCrnt, PosEqual + 1), "|")
        NumValue = NumValue + UBound(ValueList) - LBound(ValueList) + 1
        ColValidateCrnt = ColValidateCrnt + 1
      Loop
      NumCond = NumCond + ColValidateCrnt - 2
      RowValidateCrnt = RowValidateCrnt + 1
    Loop

    ' Size arrays
    ReDim ColRule(ColValMin To ColValMax)
    ReDim Rule(1 To RowValidateCrnt - 2)
    ReDim ValueCell(1 To NumValue)
    ReDim Cond(1 To NumCond)

    InxRuleCrnt = 0
    InxValueCellCrnt = 0
    InxCondCrnt = 0

    ' Extract rules in column number order
    For ColValCrnt = ColValMin To ColValMax
      ' The first rule for this column, if any, will be the
      ' next entry in the Rule table
      ColRule(ColValCrnt).InxRule1 = InxRuleCrnt + 1
      ' If there are no rules for this column, the last rule index
      ' will be less than the first rule undex
      ColRule(ColValCrnt).InxRuleL = InxRuleCrnt
      RowValidateCrnt = 2
      Do While True
        PermittedCrnt = .Cells(RowValidateCrnt, 1).Value
        If PermittedCrnt = "" Then
          Exit Do
        End If
        PosEqual = InStr(1, PermittedCrnt, "=")
        ColCodeCrnt = Mid(PermittedCrnt, 1, PosEqual - 1)
        ColNumCrnt = Range(ColCodeCrnt & "1").Column
        If ColNumCrnt = ColValCrnt Then
          ' This rule is for the current column
          InxRuleCrnt = InxRuleCrnt + 1
          ' This could be the last rule for this column so
          ' store its index against the column
          ColRule(ColValCrnt).InxRuleL = InxRuleCrnt
          ' The first value for this rule will be the next
          ' entry in the Value table
          Rule(InxRuleCrnt).InxValue1 = InxValueCellCrnt + 1
          ValueList = Split(Mid(PermittedCrnt, PosEqual + 1), "|")
          ' Save each permitted value in the Value table
          For InxValueListCrnt = LBound(ValueList) To UBound(ValueList)
            InxValueCellCrnt = InxValueCellCrnt + 1
            ValueCell(InxValueCellCrnt) = ValueList(InxValueListCrnt)
          Next
          ' Record the index of the last permitted value for this rule
          Rule(InxRuleCrnt).InxValueL = InxValueCellCrnt
          ' The first condition for this rule, if any, will be the next
          ' entry in the Condition table
          Rule(InxRuleCrnt).InxCond1 = InxCondCrnt + 1
          ' If there are no conditions for this rule, the last condition
          ' index will be less than the first condition undex
          Rule(InxRuleCrnt).InxCondL = InxCondCrnt
          ColValidateCrnt = 2
          Do While True
            ConditionCrnt = .Cells(RowValidateCrnt, ColValidateCrnt).Value
            If ConditionCrnt = "" Then
              Exit Do
            End If
            InxCondCrnt = InxCondCrnt + 1
            PosEqual = InStr(1, ConditionCrnt, "=")
            ColCodeCrnt = Mid(ConditionCrnt, 1, PosEqual - 1)
            ColNumCrnt = Range(ColCodeCrnt & "1").Column
            ' Store the column for this condition
            Cond(InxCondCrnt).Col = ColNumCrnt
            ' The first value for this condition will be the next
            ' entry in the Value table
            Cond(InxCondCrnt).InxValue1 = InxValueCellCrnt + 1
            ValueList = Split(Mid(ConditionCrnt, PosEqual + 1), "|")
            For InxValueListCrnt = LBound(ValueList) To UBound(ValueList)
              InxValueCellCrnt = InxValueCellCrnt + 1
              ValueCell(InxValueCellCrnt) = ValueList(InxValueListCrnt)
            Next
            ' Record last value for this condition
            Cond(InxCondCrnt).InxValueL = InxValueCellCrnt
            ColValidateCrnt = ColValidateCrnt + 1
          Loop
          ' Record last condition for this rule
          Rule(InxRuleCrnt).InxCondL = InxCondCrnt
        End If
        RowValidateCrnt = RowValidateCrnt + 1
      Loop
    Next
  End With

  Debug.Print "    Rules per Column table"
  Debug.Print "    C RR RR"
  For ColValCrnt = ColValMin To ColValMax
    Debug.Print "    " & ColValCrnt & " " & _
                Right(" " & ColRule(ColValCrnt).InxRule1, 2) & " " & _
                Right(" " & ColRule(ColValCrnt).InxRuleL, 2)
  Next
  Debug.Print
  Debug.Print "    Rule table"
  Debug.Print "    I VV VV CC CC"
  For InxRuleCrnt = 1 To UBound(Rule)
    Debug.Print "    " & InxRuleCrnt & " " & _
                         Right(" " & Rule(InxRuleCrnt).InxValue1, 2) & " " & _
                         Right(" " & Rule(InxRuleCrnt).InxValueL, 2) & " " & _
                         Right(" " & Rule(InxRuleCrnt).InxCond1, 2) & " " & _
                         Right(" " & Rule(InxRuleCrnt).InxCondL, 2) & " "
  Next
  Debug.Print
  Debug.Print "    Condition table"
  Debug.Print "     I C VV VV"
  For InxCondCrnt = 1 To UBound(Cond)
    Debug.Print "    " & Right(" " & InxCondCrnt, 2) & " " & _
                         Cond(InxCondCrnt).Col & " " & _
                         Right(" " & Cond(InxCondCrnt).InxValue1, 2) & " " & _
                         Right(" " & Cond(InxCondCrnt).InxValueL, 2)
  Next
  Debug.Print
  Debug.Print "    Value table"
  Debug.Print "    ";
  For InxValueCellCrnt = 1 To UBound(ValueCell)
    Debug.Print "E" & Right(" " & InxValueCellCrnt, 2) & "=" & _
                Left(ValueCell(InxValueCellCrnt) & "    ", 5);
    If (InxValueCellCrnt Mod 10) = 0 Then
      Debug.Print
      Debug.Print "    ";
    End If
  Next

结束子

于 2012-05-02T09:58:06.260 回答
0

如果您要坚持使用命名范围,则应使用 INDEX 和 COUNTA 公式使命名范围动态化。这样您就可以将记录添加到列表中,并且命名范围将自动调整。但接下来我会告诉你不要使用命名范围。

这种链接数据验证非常适合简单的链表。但是您的情况非常简单,我认为您需要从 DV 转向 ActiveX 控件,可能在用户窗体上。

您可以达到几个复杂程度。一方面是你现在所拥有的。另一方面,一切都存在于数据库中,而 Excel 是适当关系数据库的计算引擎/前端。您可能最终会处于这两者的中间。

我没有足够的信息给你一个真正相关、详细的答案,所以我会做出一堆假设,你必须认识到它何时不适合你的情况。我认为您需要创建一个用户表单来处理数据输入。用户窗体上的列表框/组合框将通过代码动态更新。用户表单将通过功能区、右键菜单或“编辑”超链接调用。用户窗体将填充当前选定的行。

然后你会有一个复制和粘贴选项。用户可以将单个项目复制并粘贴到用户表单中,您的代码将验证它们。或者他们可以复制代码将验证的完整信息记录。

不要回避 Access 作为后端。我的大部分项目都是由 Excel 控制的 Jet 数据库。Excel 是计算引擎、输入机制和报告机制。

于 2012-04-30T19:29:58.043 回答