You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell脚本插入Oracle时SERVER_NAME非空错误排查请求

问题

我编写了一段PowerShell脚本,用于收集SQL Server实例相关信息并写入作为环境信息仓库的Oracle数据库。执行脚本时,系统抛出错误提示Column SERVER_NAME cannot be null,但输出对应变量时可以看到ServerName字段存在有效值。已确认Oracle连接正常,操作账号具备足够的插入/更新权限,请求帮忙排查脚本中的错误原因。

相关脚本

#Import necessary modules
Import-Module DBAtools

# Path to the Oracle.ManagedDataAccess.dll file
$oracleDllPath = "D:\oracle\product\12.2.0\client_1\odp.net\managed\common\Oracle.ManagedDataAccess.dll"
# Load the assembly
Add-Type -Path $oracleDllPath

# Initialize logging
$logFile = "D:\PowerShell\log.txt"
Start-Transcript -Path $logFile

WRITE-HOST "WE ARE HERE-1"
try {
    # Step 1: Get all instance names from Central Management Studio and place them in a variable called $Instances
    #$CMSInstance = "CentralManagementServerInstanceName"
    #$Instances = Get-DbaCmsRegServer -SqlInstance $CMSInstance -Group "" | Select-Object -ExpandProperty Name
    $Instances = "SQLInst1","SQLInst2"

    # Initialize arrays to hold accessible and inaccessible instances
    $AccessibleInstances = @()
    $InaccessibleInstances = @()

    # Step 2: Ensure each instance is accessible
    foreach ($Instance in $Instances) {
        try {
            # Check if the instance is accessible
            Test-DbaConnection -SqlInstance $Instance -ErrorAction Stop
            # If successful, add to AccessibleInstances
            $AccessibleInstances += $Instance
        } catch {
            # If not accessible, add to InaccessibleInstances
            $InaccessibleInstances += $Instance
        }
    }
WRITE-HOST "WE ARE HERE-2"
    # Initialize arrays to hold data
    $serverStageData = @()
    $diskDriveStageData = @()

    # Step 5: Loop through each entry in $AccessibleInstances and gather required information
    foreach ($Instance in $AccessibleInstances) {
        # Example DBAtools commands (replace with actual commands as needed)
        $server = Connect-DbaInstance -SqlInstance $Instance

        # Gathering Operating System Information
        $os = Get-DbaOperatingSystem -ComputerName $server.ComputerName
        $computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $server.ComputerName
        $cpu = Get-CimInstance -ClassName Win32_Processor -ComputerName $server.ComputerName
        $networkConfig = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -ComputerName $server.ComputerName | Where-Object { $_.IPAddress -ne $null } 
WRITE-HOST "WE ARE HERE-3"
        # Creating the output object
        $serverInfo = [PSCustomObject]@{
            ServerName               = $server.ComputerName
            OSName                   = $os.OSVersion
            OSVersionCode            = $os.Version
            OSVendorName             = $os.Manufacturer
            OSPatchLevelCode         = $os.Build
            CPUVendorName            = $cpu.Name[0]
            CPUManufacturerName      = $cpu.Manufacturer[0]
            CPUFrequencyMSR          = $cpu.MaxClockSpeed[0]
            PhysicalCoreCount        = ($cpu | Measure-Object -Property NumberOfCores -Sum).Sum
            TotalCoreCount           = ($cpu | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
            PhysicalRAMGBMSR         = [math]::round($computerSystem.TotalPhysicalMemory / 1GB, 2)
            RAMGBMSR                 = [math]::round($computerSystem.TotalPhysicalMemory / 1GB, 2)
            DomainName               = $computerSystem.Domain
            OperationalStatusCode    = $computerSystem.Status
            ManagedByServerName      = 'CMS'
            IPAddress                = $networkConfig.IPAddress[0]
            MANAGED_BY_DATABASE_NAME = 'DBAAdmin'
        }
WRITE-HOST "WE ARE HERE-4"
        #2.TABLE DISK_DRIVE_STAGE
        $diskDriveInfo = Get-DbaDiskSpace -ComputerName $server.ComputerName | Select-Object @{label='SERVER_NAME';expression={$_.ComputerName}}, @{label='DISK_DRIVE_NAME';expression={$_.Label}}, @{label='CAPACITY_GB_MSR';expression={$_.SizeInGB}}, @{label='USED_GB_MSR';expression={$_.SizeInGB - $_.FreeInGB}}, @{label='FREE_SPACE_GB_MSR';expression={$_.FreeInGB}}, @{label='DISK_DRIVE_LETTER_CODE';expression={$_.Name}}, @{label='LINUX_MOUNT_POINT_PATH';expression={'N/A'}} 

        # Store the gathered information in the respective variables
        $serverStageData += $serverInfo
        #$diskDriveStageData += $diskDriveInfo
        
    }
    

    # Step 6: Connect to the Oracle database
    $oracleConnString = "User Id=myuser;Password=mypwd;Data Source=myoracledb"
    $conn = New-Object Oracle.ManagedDataAccess.Client.OracleConnection($oracleConnString)
WRITE-HOST "WE ARE HERE-5"
    try {
        $conn.Open()
WRITE-HOST "WE ARE HERE-6"
        Write-Host "Successfully connected to Oracle database."
    } catch {
WRITE-HOST "WE ARE HERE-7"
        Write-Host "Failed to connect to Oracle database. Exiting script."
        Stop-Transcript
        Exit 1
    }
WRITE-HOST "WE ARE HERE-8"
    # Step 7: Function to update or insert records in Oracle tables.
function Upsert-OracleTable {
    param (
        [string]$tableName,
        [array]$data,
        [array]$columns
    )

        foreach ($record in $data) {
            # Ensure no null values are inserted by checking each value
            foreach ($column in $columns) {
                if ([string]::IsNullOrEmpty($record.$column)) {
                    Write-Host "Column $column cannot be null. Record: $record"
                    throw "Column $column cannot be null"
                }
            }

            # Build the query to check if the record exists by comparing all columns
            $whereClause = @()
            foreach ($column in $columns) {
                $whereClause += "$column = :$column"
            }
            $whereClause = $whereClause -join " AND "

            $checkCmd = $conn.CreateCommand()
            $checkCmd.CommandText = "SELECT COUNT(*) FROM $tableName WHERE $whereClause"
            $checkCmd.Parameters.Clear()
            foreach ($column in $columns) {
                $checkCmd.Parameters.Add((New-Object Oracle.ManagedDataAccess.Client.OracleParameter($column, $record.$column)))
            }
            $exists = $checkCmd.ExecuteScalar()

            if ($exists -ne 0) {
                # Build the update clause
                $updateClause = ""
                $updateNeeded = $false
                foreach ($column in $columns) {
                    $currentValueCmd = $conn.CreateCommand()
                    $currentValueCmd.CommandText = "SELECT $column FROM $tableName WHERE $whereClause"
                    $currentValueCmd.Parameters.Clear()
                    foreach ($col in $columns) {
                        $currentValueCmd.Parameters.Add((New-Object Oracle.ManagedDataAccess.Client.OracleParameter($col, $record.$col)))
                    }
                    $currentValue = $currentValueCmd.ExecuteScalar()

                    if ($currentValue -ne $record.$column) {
                        $updateClause += "$column = :$column, "
                        $updateNeeded = $true
                    }
                }

                if ($updateNeeded) {
                    # Remove the last comma
                    $updateClause = $updateClause.Substring(0, $updateClause.Length - 2)

                    # Build and execute the update command
                    $updateCmd = $conn.CreateCommand()
                    $updateCmd.CommandText = "UPDATE $tableName SET $updateClause WHERE $whereClause"
                    $updateCmd.Parameters.Clear()
                    foreach ($column in $columns) {
                        $updateCmd.Parameters.Add((New-Object Oracle.ManagedDataAccess.Client.OracleParameter($column, $record.$column)))
                    }
                    $updateCmd.ExecuteNonQuery()
                }
            } else {
                # Build and execute the insert command
                $columnsStr = ""
                $valuesStr = ""
                $insertCmd = $conn.CreateCommand()
                foreach ($column in $columns) {
                    $columnsStr += "$column, "
                    $valuesStr += ":$column, "
                    $insertCmd.Parameters.Add((New-Object Oracle.ManagedDataAccess.Client.OracleParameter($column, $record.$column)))
                }

                # Remove the last commas
                $columnsStr = $columnsStr.Substring(0, $columnsStr.Length - 2)
                $valuesStr = $valuesStr.Substring(0, $valuesStr.Length - 2)

                $insertCmd.CommandText = "INSERT INTO $tableName ($columnsStr) VALUES ($valuesStr)"
                Write-Host "Insert Command: $insertCmd.CommandText"
                foreach ($param in $insertCmd.Parameters) {
                    Write-Host "Parameter: $($param.ParameterName) = $($param.Value)"
                }
                $insertCmd.ExecuteNonQuery()
            }
        }
    }



WRITE-HOST "WE ARE HERE-14"
    # Update or insert records in Oracle tables with specific columns
    $serverColumns = @("SERVER_NAME", "OPERATING_SYSTEM_NAME", "OPERATING_SYSTEM_VERSION_CODE", "OPERATING_SYSTEM_VENDOR_NAME", "OPERATING_SYSTEM_PTCH_LVL_CODE", "CPU_VENDOR_NAME", "CPU_MANUFACTURER_NAME", "CPU_FREQUENCY_MSR", "PHYSICAL_CORE_CNT", "TOTAL_CORE_CNT", "PHYSICAL_RAM_GB_MSR", "RAM_GB_MSR", "DOMAIN_NAME", "OPERATIONAL_STATUS_CODE", "MANAGED_BY_SERVER_NAME", "IP_ADDRESS", "MANAGED_BY_DATABASE_NAME")
    #$diskDriveColumns = @("SERVER_NAME", "DISK_DRIVE_NAME", "CAPACITY_GB_MSR", "FREE_SPACE_GB_MSR", "DISK_DRIVE_LETTER_CODE", "LINUX_MOUNT_POINT_PATH")
WRITE-HOST "WE ARE HERE-15"
    # Specify the schema name for each table
    $serverTable = "MySchema.SERVER_STAGE_T"
   # $diskDriveTable = "MyShema.DISK_DRIVE_STAGE_T"
WRITE-HOST "WE ARE HERE-16" 

echo $serverColumns
echo $serverStageData
echo $serverTable
   
    Upsert-OracleTable -tableName $serverTable -data $serverStageData -columns $serverColumns
    #Upsert-OracleTable -tableName $diskDriveTable -data $diskDriveStageData -columns $diskDriveColumns
WRITE-HOST "WE ARE HERE-17"
} catch {
    Write-Host "An error occurred: $_"
    Stop-Transcript
    Exit 1
} finally {
    # Close the Oracle connection
    $conn.Close()
    Stop-Transcript
}

错误日志

Column SERVER_NAME cannot be null.
Record: @{ServerName=Server1; OSName=Microsoft Windows Server 2016 Standard; OSVersionCode=10.0.14393; OSVendorName=Microsoft Corporation; OSPatchLevelCode=14393; CPUVendorName=Intel(R) Xeon(R) Platinum 8170 CPU @ 2.10GHz; CPUManufacturerName=GenuineIntel; CPUFrequencyMSR=2095; PhysicalCoreCount=4; TotalCoreCount=4; PhysicalRAMGBMSR=48; RAMGBMSR=48; DomainName=my.domain; OperationalStatusCode=OK; ManagedByServerName=CMSServer; IPAddress=12.11.196.258; MANAGED_BY_DATABASE_NAME=DBAAdmin}
PS>TerminatingError(): "Column SERVER_NAME cannot be null"
An error occurred: Column SERVER_NAME cannot be null

问题原因与解决方案

核心原因

脚本存在属性名与Oracle列名不匹配的问题:

  • 构建的$serverInfo对象中,服务器名称的属性是ServerName(驼峰命名)
  • 传入Upsert-OracleTable函数的列名数组$serverColumns里,对应的Oracle列是SERVER_NAME(大写下划线命名)
  • 函数通过$record.$column取值时,会尝试访问对象的SERVER_NAME属性,但该属性不存在,返回null,触发非空校验错误。

修复方案

方案1:修改PS对象属性名匹配Oracle列名

直接调整$serverInfo的属性名,与Oracle列名完全一致:

$serverInfo = [PSCustomObject]@{
    SERVER_NAME               = $server.ComputerName
    OPERATING_SYSTEM_NAME     = $os.OSVersion
    OPERATING_SYSTEM_VERSION_CODE = $os.Version
    OPERATING_SYSTEM_VENDOR_NAME = $os.Manufacturer
    OPERATING_SYSTEM_PTCH_LVL_CODE = $os.Build
    CPU_VENDOR_NAME            = $cpu.Name[0]
    CPU_MANUFACTURER_NAME      = $cpu.Manufacturer[0]
    CPU_FREQUENCY_MSR          = $cpu.MaxClockSpeed[0]
    PHYSICAL_CORE_CNT        = ($cpu | Measure-Object -Property NumberOfCores -Sum).Sum
    TOTAL_CORE_CNT           = ($cpu | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
    PHYSICAL_RAM_GB_MSR         = [math]::round($computerSystem.TotalPhysicalMemory / 1GB, 2)
    RAM_GB_MSR                 = [math]::round($computerSystem.TotalPhysicalMemory / 1GB, 2)
    DOMAIN_NAME               = $computerSystem.Domain
    OPERATIONAL_STATUS_CODE    = $computerSystem.Status
    MANAGED_BY_SERVER_NAME      = 'CMS'
    IP_ADDRESS                = $networkConfig.IPAddress[0]
    MANAGED_BY_DATABASE_NAME = 'DBAAdmin'
}

方案2:在Upsert函数中添加属性名映射

如果不想修改PS对象结构,可在函数内添加映射逻辑,将Oracle列名对应到PS对象属性:

# 在Upsert-OracleTable函数的foreach ($record in $data)循环前添加映射表
$columnMapping = @{
    "SERVER_NAME" = "ServerName"
    "OPERATING_SYSTEM_NAME" = "OSName"
    "OPERATING_SYSTEM_VERSION_CODE" = "OSVersionCode"
    "OPERATING_SYSTEM_VENDOR_NAME" = "OSVendorName"
    "OPERATING_SYSTEM_PTCH_LVL_CODE" = "OSPatchLevelCode"
    "CPU_VENDOR_NAME" = "CPUVendorName"
    "CPU_MANUFACTURER_NAME" = "CPUManufacturerName"
    "CPU_FREQUENCY_MSR" = "CPUFrequencyMSR"
    "PHYSICAL_CORE_CNT" = "PhysicalCoreCount"
    "TOTAL_CORE_CNT" = "TotalCoreCount"
    "PHYSICAL_RAM_GB_MSR" = "PhysicalRAMGBMSR"
    "RAM_GB_MSR" = "RAMGBMSR"
    "DOMAIN_NAME" = "DomainName"
    "OPERATIONAL_STATUS_CODE" = "OperationalStatusCode"
    "MANAGED_BY_SERVER_NAME" = "ManagedByServerName"
    "IP_ADDRESS" = "IPAddress"
    "MANAGED_BY_DATABASE_NAME" = "MANAGED_BY_DATABASE_NAME"
}

# 替换原有的非空校验逻辑
foreach ($column in $columns) {
    $propertyName = $columnMapping[$column] ?? $column
    if ([string]::IsNullOrEmpty($record.$propertyName)) {
        Write-Host "Column $column cannot be null. Record: $record"
        throw "Column $column cannot be null"
    }
}

# 后续所有使用$record.$column的地方,都替换为$record.$propertyName

额外建议

  • 可在脚本中添加属性名校验逻辑,提前发现不匹配问题,避免运行时错误
  • 统一使用一种命名规范(如全大写下划线),减少此类映射错误

内容的提问来源于stack exchange,提问作者Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:45:55