0

Below is a working SSMS query, meaning it will run and produce the requested results in SSMS with no problems. But, I am trying to get this query to run in PowerShell in order to create a daily automated script. PowerShell simply puts the output of this SSMS query into an array, then transfers it to a .txt file. When it is run in PowerShell, it kicks back:

Exception calling "Fill" with "1" argument(s)
"The multi-part identifier "l.cust_login" could not be bound"
"The multi-part identifier "l.user_id" could not be bound"
"The multi-part identifier "c.cust_full_name" could not be bound"
"The multi-part identifier "a.login_type" could not be bound"
"The multi-part identifier "a.flag_login" could not be bound"
"The multi-part identifier "a1.flag_login" could not be bound"
"The multi-part identifier "a2.flag_login" could not be bound"

Here is the query that mind you, works in SSMS just fine!

    SELECT
    rtrim(l.cust_login)+':'+
    rtrim(l.user_id)+':'+
    rtrim(c.cust_full_name)+':'+
    rtrim(a.login_type)+':'+
    
    CASE
    WHEN a1.flag_login IS NOT NULL THEN 'flagged'
    ELSE ''
    END+':'+

    CASE
    WHEN a2.flag_login IS NOT NULL THEN 'co-flagged'
    ELSE ''
    END
    
    FROM accounts a
    INNER JOIN customer c ON a.owner_id = c.owner_id
    INNER JOIN logins l ON a.cust_login = l.cust_login

    --problematic joins
    LEFT OUTER JOIN accounts a1 ON (a.cust_login = a1.cust_login AND a1.flag_login = 22)
    LEFT OUTER JOIN accounts a2 ON (a.cust_login = 
    a2.cust_login AND a2.flag_login IN (45,46))
    
    WHERE a.flag_login = 55
    ORDER BY 1

Now, ive been able to find where PowerShell gets upset, and its with the recursive table joins. If I leave them out, then the query runs fine in PowerShell. Below is an example query that I made that does not include the needed JOINS and CASE

    SELECT
    rtrim(l.cust_login)+':'+
    rtrim(l.user_id)+':'+
    rtrim(c.cust_full_name)+':'+
    rtrim(a.login_type)+':'
    
    --missing CASE statements here
    
    FROM accounts a
    INNER JOIN customer c ON a.owner_id = c.owner_id
    INNER JOIN logins l ON a.cust_login = l.cust_login
    
    --missing outer joins here
        
    WHERE a.flag_login = 55
    ORDER BY 1

I actually had the bright idea of converting the problematic query to include temp tables, I sadly have the same issue! Even when I define separate temp tables in place of the recursive joins. As well as CTEs!

I suppose my main question is, does anyone know a workaround that powershell may like for the problematic outer joins. As with the error, it seems as if powershell is having trouble distinguishing between accounts a, accounts a1, and accounts a2. Even though when ran solely in SSMS, there is no issue.

I know this is a lot, I added the powershell script due to request. Sorry for the long post.

PowerShell SQL module:

function Invoke-table1SQL {
    param ([string] $FileLocation)

    if (!(Test-Path $FileLocation)) {
        Write-Host -BackgroundColor:Black -ForegroundColor:Red "File does not exist: $FileLocation"
        exit
    }

    $config_file = "$PSScriptRoot\ConnectionStrings.config"
    [xml] $appSettings = (Get-Content $config_file)
    $connectionString = $($appSettings.connectionStrings.add | Where-Object { $_.name -eq "ConnectionString" }).connectionString
    
    #db connection
    $SqlConnection = New-Object System.Data.SqlClient.SqlConnection
    $SqlConnection.ConnectionString = $connectionString
    
    #run sql cmd
    $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
    $SqlCmd.CommandTimeout = 1200
    $SqlCmd.Connection = $SqlConnection
    $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
    $SqlCmd.CommandText = get-content $FileLocation
    $SqlAdapter.SelectCommand = $SqlCmd
    $DataSet = New-Object System.Data.DataSet
    $SqlAdapter.Fill($DataSet)
    
    return $DataSet
}

export-modulemember -function Invoke-table1SQL

And here is the actual script using the module:

Import-Module $PSScriptRoot\Invoke-table1SQL.psm1 -Force

#declare
$queryLocation = "[location of query]"
$results = Invoke-table1SQL -FileLocation $queryLocation

#return index '0' in $dataset - $results and clean up output file
$outfile = "[txt file output location] " 
$results.Tables[0].Rows | Format-Table -HideTableHeaders | Out-File -filePath $outfile
Ransome
  • 101
  • 6
  • 2
    [Bad Habits to Kick : Using table aliases like (a, b, c) or (t1, t2, t3)](https://sqlblog.org/2009/10/08/bad-habits-to-kick-using-table-aliases-like-a-b-c-or-t1-t2-t3) – Thom A Dec 27 '21 at 21:04
  • 2
    `b` is for customer when `c` is for Logins, for example, is *very* confusing. Why is `customer` not `c`? `l` would make much for sense for `logins`. – Thom A Dec 27 '21 at 21:05
  • Your right, it is a bad, lazy habit... – Ransome Dec 27 '21 at 21:06
  • As an aside, there's nothing *recursive* about joining to the same table twice with different criteria. – Stu Dec 27 '21 at 21:06
  • I'll bet the quotes in your SQL might be tripping up PowerShell's parser - or you need a `USE` statement, or your connection-string isn't using the correct `Initial Catalog=` parameter. – Dai Dec 27 '21 at 21:10
  • Does it work if you just remove all the `ELSE ''`? – LukStorms Dec 27 '21 at 21:13
  • @LukStorms Sadly that did not work. – Ransome Dec 27 '21 at 21:18
  • @Larnu I fixed the lazy identifiers : ) – Ransome Dec 27 '21 at 21:19
  • @Stu The `ELSE` should handle any nulls I thought? – Ransome Dec 27 '21 at 21:21
  • Can you provide the powershell script then? – Martin Smith Dec 27 '21 at 21:25
  • @Dai I'm not sure about the quotes, as like I said, the second query that includes quotes works fine in powershell. Adding a `USE` (db name) at the top did not make a difference sadly. The connection string is set to use the right database. I would expect the second query to not work either, if that was a problem. – Ransome Dec 27 '21 at 21:25
  • One bad practice is that you aren't schema qualifying your tables so they could resolve to entirely different objects without the expected columns if run under a different user – Martin Smith Dec 27 '21 at 21:28
  • @MartinSmith I can provide the script, one moment : ) – Ransome Dec 27 '21 at 21:34
  • 2
    I'm not a frequent powershell user but `$SqlCmd.CommandText = get-content $FileLocation` looks suspicious to me. The documentation says "For files, the content is read one line at a time and returns a collection of objects, each of which represents a line of content." so I am suspicious that the command text may just be set to the first such item (so truncating the query to before where those aliases are later introduced) – Martin Smith Dec 27 '21 at 22:20
  • @MartinSmith Wow... I cannot thank you enough. You do not want to know how many days I have spent trying to figure out the issue. Too many days.... You were right, this was the issue. While I will spend some time looking around for a better alternative to `Get-Content`, for the time being I just replaced the entire query into powershell in `Get-Contents` and `$FileLocations` place to test, and it worked. Please go ahead and write on this post with your comment as an answer, so that I may mark it as the solution. I dont think I can mark comments as a solution. – Ransome Dec 27 '21 at 22:37
  • 1
    I'll leave it to someone that knows the vagaries of powershell to provide a fuller answer – Martin Smith Dec 27 '21 at 22:42
  • 1
    You can try passing `-Raw` to `Get-Content` have it place the comments of the file in a scalar variable (instead of an array) – Ben Thul Dec 28 '21 at 07:58
  • @BenThul Thanks, Adding `-raw` to `get-content` seems to work. Its a lot nicer than pasting the entire query into the script. I'm not too sure why adding that makes a difference. I suppose it reads the query as a whole now? Rather than line by line? – Ransome Dec 30 '21 at 21:46
  • The documentation says as much: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-content?view=powershell-7.2 – Ben Thul Dec 31 '21 at 11:00

1 Answers1

0

Are you able to use the dbatools module? They have an Invoke-DbaQuery command that can run a query from the text file. I'd be curious if this worked as it is pretty robust.

Something like this maybe?

Invoke-DbaQuery -SqlInstance serverName -File $QuerySqlFile
Jess
  • 116
  • 1
  • 5