Trouble converting file stored in SQL table to file stored on disk (missing content) - sql

I have a database table with following columns (among others):
emailAttachment
emailAttachmentFileName
I am using the following Powershell to convert a .csv file to binary, to store in emailAttachment (defined as varbinary(max)):
$("0x$(([Byte[]](Get-Content -Encoding Byte -Path c:\temp\test.csv) | ForEach-Object ToString X2) -join '')")
As far as I can tell, that works fine.
To retrieve the data, I run this SQL query:
$query = #'
SELECT
*
FROM
[dbo].[mydatabase].[mytable]
WHERE
id = 1
'#
$file = Invoke-Sqlcmd -Query $query -ServerInstance server.domain.local -Credential (Get-Credential)
Now that I have the bytes from SQL, I want to write it to a file:
[IO.File]::WriteAllBytes($file.emailAttachmentFileName, $file.emailAttachment)
That creates the file with the correct name and some of the rows of the .csv, but not all of them (63 instead of 464).
I also tried both, but they have the same result:
[IO.File]::WriteAllLines($file.emailAttachmentFileName, [System.Text.Encoding]::ASCII.GetString($file.emailAttachment))
[IO.File]::WriteAllText($file.emailAttachmentFileName, [System.Text.Encoding]::ASCII.GetString($file.emailAttachment))
What am I doing wrong here?

Related

Powershell - Can I scrape SQL file and compare results?

I am fairly new to Powershell and have run into a bit of a pickle.
I am trying to scrape a SQL script in txt file format. My goal is to check if every created (volatile) table is also dropped in the same file. And if not, output the name of the table which is not dropped in the SQL script.
On top of that I would like check if the "drop" of the table is AFTER "create volatile table" and not BEFORE, because that would be wrong syntax. Do you think it's somehow possible?
I tried to do it by extracting lines of codes where is the create table string, then using regex to get name(string) of the table and saving it to variable. I did the same with the "drop table...". Now I am trying to compared those two (string) variables by converting them to list, object or whatever. I feel like I am in dead end.
$vtMatch = Get-ChildItem "$packagepath\Scripts\" -Recurse | Select-String -Pattern "create multiset volatile table VT_","create multiset volatile table vt_"
$vtMatch = $vtMatch.line
$vt = $vtMatch | Select-String 'vt_(.*?)(?=,)' -AllMatches | Select-Object -Expand matches | ForEach-Object {$_.value}
$dropMatch = Get-ChildItem "$packagepath\Scripts\" -Recurse | Select-String -Pattern "drop table VT_","drop table vt_"
$dropMatch = $dropMatch.line
$drop = $dropMatch | Select-String 'vt_(.*?)(?=;)' -AllMatches | Select-Object -Expand matches | ForEach-Object {$_.value}
$missing = Compare-Object -ReferenceObject $vt -DifferenceObject $drop -Property item
$missing
The variable $vt contains these strings:
vt_R_transaction_Dm
vt_h_bank_account
vt_DD_Agreement_Detail_1
vt_DD_Agreement_Detail_2
vt_DM_Agreement_Detail_Prebase
vt_DM_Agreement_Detail_RWA
vt_DM_Agreement_Detail_1
vt_posted_transaction
vt_DM_Corp_Objective_Specific_MAT
vt_DM_Party_Detail
And variable $drop contains these strings:
vt_DD_Agreement_Detail_1
vt_DD_Agreement_Detail_2
vt_DM_Agreement_Detail_Prebase
vt_DM_Agreement_Detail_RWA
vt_DM_Agreement_Detail_1
vt_DM_Corp_Objective_Specific_MAT
vt_posted_transaction
vt_DM_Party_Detail

Timestamp issues with Powershell

I have a small powershell script that pulls the last hour of punch data from a sql db, it then outputs that data to a .csv file. The script is working, but the timestamp is like this:
hh:mm:ss.xxx, i need it to be only hh:mm, Any help would be greatly appreciated!
Below is the script and a snippet of the output:
sqlcmd -h-1 -S ZARIRIS\IRIS -d IA3000SDB -Q "SET NOCOUNT ON; Select Distinct TTransactionLog_1.DecisionTimeInterval,
TTransactionLog_1.UserID, TTransactionLog_1.OccurDateTime, TTransactionLog_1.StableTimeInterval
From TTransactionLog_1
Inner join TSystemLog1 On TTransactionLog_1.NodeID=TSystemLog1.NodeID
Inner join TUser On TTransactionLog_1.UserID=Tuser.UserID
where TSystemLog1.NodeID = 3 and TTransactionLog_1.OccurDateTime >= dateadd(HOUR, -1, getdate())" -s "," -W -o "C:\atr\karen\adminreport3.csv"
Get-Content "C:\ATR\Karen\adminreport3.csv" | ForEach-Object {$_ -replace "44444444","IN PUNCH"} | ForEach-Object {$_ -replace "11111111","OUT PUNCH"} | Set-Content "C:\ATR\Karen\punchreport1.csv" -Force
Output: (where i need the hh:mm format, it needs to read 12:08, not 12:08:19.000)
112213,2022-10-31 12:08:19.000,OUT PUNCH
It would probably be best if your script were to write out a date formatted the way you want in the first place,
but if that's not an option, you really should consider using Import-Csv and Export-Csv to manipulate the data inside.
If the standard quoted csv output is something you don't want, please see this code to safely remove the quotes where possible.
Having said that, here's one way of doing it in a line-by-line fashion:
Get-Content "C:\ATR\Karen\adminreport3.csv" | ForEach-Object {
$line = $_ -replace "44444444","IN PUNCH" -replace "11111111","OUT PUNCH"
$fields = $line -split ','
# reformat the date by first parsing it out as DateTime object
$fields[1] = '{0:yyyy-MM-dd HH:mm}' -f [datetime]::ParseExact($fields[1], 'yyyy-MM-dd HH:mm:ss.fff',$null)
# or use regex on the date and time string as alternative
# $fields[1] = $fields[1] -replace '^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}).*', '$1'
# rejoin the fields with a comma
$fields -join ','
} | Set-Content "C:\ATR\Karen\punchreport1.csv" -Force

Powershell | How to make an if statement that compares date from SQL Table with Date given in script

Background information: So I am writing a Powershell script to clean up a SQL Server Database and I have a question on how to perform an if statement in this very specific case.
So the script works as follows:
The Script connects to the SQL Server Database
It get's the current date -5 days and converts it to unix Epoch time
It takes that Epoch date and cuts off the decimals
Then adds 3 zero's because the Database requires 13 numbers (Not sure why who ever set the database up decided to do this but it is what it is)
Then comes the if statement <-- my question
Define SQL Query
Run the sql query
The Question: So I want to have an if statement that compares the converted unix epoch date with the epoch date from the SQL Server
Database Table and if the date is less or equal then the entries are deleted.
My code is currently as follows:
#### Variables ####
# Sorry for having to censor the variable inputs, wouldn't wanna leak this sensitive information everywhere.
$UID = 'uid'
$PASS = 'pass'
$SI = 'serverinstance'
$DB = 'database'
$Table = 'table'
$Table_age = 'date_column'
$Age = 5 # Maximum age of an entry
$DateDecimal = ',' # Depends on the PC but this might have to be changed to a .period instead
$EQLcompat = '000' # EQL Database compatibility for the Epoch time
#### Get date and convert to Epoch ####
$Date = Get-Date $((Get-Date).AddDays(-$Age).ToUniversalTime()) -UFormat +%s
#Substring the decimal values
$DateCut = $Date.Substring(0, $Date.IndexOf($DateDecimal))
#Make Epoch date EQL Database compatible
$unixTime = $DateCut + $EQLcompat
#### if statement in Question ####
if ($Table -le $unixTime){
# SQL Query
$SQLquery = "
SELECT TOP (1) *
FROM [$Table]
"}
else{
Write-Host 'Nope, not this way'
}
#### Run SQL Query ####
Invoke-Sqlcmd -serverinstance $SI -Database $DB -username $UID -password $PASS -query $SQLquery
Does anyone have any idea how to do this if statement to make this work? Thank you very much!
EDIT 1:
I've tried if (+ $Table -le $unixTime){ however this gives me the error code;
Cannot convert value "databasename" to type "System.Int32". Error: "Input string was not in a correct format."
At C:\Users\fabstefanm.GEMEENTENET\Documents\Code\SQL Table Cleaner.ps1:35 char:5
+ if (+ $Table -le $unixTime){
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
EDIT 2:
Meant SQL Server not MySQL, my bad got the two names crossed.
EDIT 3:
I've changed the if statement to this:
#### if statement ####
if ($DB.output -le $unixTime){
# SQL Query
$SQLquery = "SELECT TOP (1) * FROM [$Table] WHERE [$Table_age] <= [$unixTime]"
Write-Host 'Yes, this way'
}
Now this does feel like I've come a step closer to solving the issue, however te error I now get is Invoke-Sqlcmd : Invalid column name '1659532862000'. So it does seem that I am not yet in the right place, does anyone know how to fix this?
I changed around the if statement slightly and now it all works. Of course it's just a select query and not yet the delete query, but that's not a big change.
This is the new if statement + SQLquery is this:
#### if statement ####
if ($DB.output -le $unixTime){
# SQL Query
$SQLquery = "SELECT TOP (10) [$Table_age] FROM [$DB].[dbo].[$Table] WHERE [$Table_age] >= $unixTime"
Write-Host 'Yes, this way'
}
So if anyone else ever comes across the need to use Powershell to run a task and then an SQL query with an if statement in it then I hope this can be of some assistance.

Find a character in a string using Powershell?

I know I could use Contains to find it but it doesn't work.
Full Story:
I have to get the PartNo, Ver, Rev from SQl db and check if they occur in the first line of the text file. I get the first line of the file and store it in $EiaContent.
The PartNo is associated with MAFN as in $partNo=Select PartNo Where MAFN=xxx. Most of the time MAFN returns one PartNo. But in some cases for one MAFN there could be multiple PartNo. So the query returns multiple PartNo(PartNo_1,PartNo_2,PartNo_3,and PartNo_4) but only one of these will be in the text file.
The issue is that each of these PartNo. is treated as a single character in PowerShell. $partNo.Length is 4. Therefore, my check If ($EiaContent.Contains("*$partNo*")) fails and it shouldn't in this case because I can see that one of the PartNo is mentioned in the file. Also, Contains wouldn't work if there was one PartNo. I use like as in If ($EiaContent -like "*$partNo*") to match the PartNo and it worked but it doesn't work when there are multiple PartNo.
Data type of $partNo is string and so is $EiaContent. The data type of PartNo. in SQL is varchar(50) collation is COLLATE SQL_Latin1_General_CP1_CI_AS
I am using PowerShell Core 7.2 and SQL 2005
Code:
$EiaContent = (Get-Content $aidLibPathFolder\$folderName\$fileName -TotalCount 1)
Write-host $EiaContent
#Sql query to get the Part Number
$partNoQuery = "SELECT PartNo FROM [NML_Sidney].[dbo].[vMADL_EngParts] Where MAFN = $firstPartTrimmed"
$partNoSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $partNoQuery
#Eliminate trailing spaces
$partNo = $partNoSql.PartNo.Trim()
If ($EiaContent.Contains("*$partNo*")) {
Write-Host "Part Matches"
}
Else {
#Send an email stating the PartNo discrepancy
}
Thank you in advance to those who try to help.
EDIT
Screenshot
[1]: https://i.stack.imgur.com/hIqJB.png
A1023 A1023MD C0400 C0400MD is the output of the variable $partNo and O40033( C0400 REV N VER 004, 37 DIA 4.5 BRAKE DRUM OP3 ) is the output of the variable $EiaContent
So the query returns multiple PartNo(PartNo_1,PartNo_2,PartNo_3,and PartNo_4) but only one of these will be in the text file.
A1023 A1023MD C0400 C0400MD is the output of the variable $partNo and O40033( C0400 REV N VER 004, 37 DIA 4.5 BRAKE DRUM OP3 ) is the output of the variable $EiaContent
So you first have to split $partNo and then for each sub string of $partNo, search for it in $EiaContent:
If ($partNo -split ' ' | Where-Object { $EiaContent.Contains( $_ ) }) {
Write-Host "Part Matches"
}
This is the generic form that most people are used to. We can simplify the query using the unary form of -split (as we split on the default separator) and use the intrinsic array method .Where() which is faster as it does not involve pipeline overhead.
If ((-split $partNo).Where{ $EiaContent.Contains( $_ ) }) {
Write-Host "Part Matches"
}
As correctly noted in comments, wildcards are not supported by the .Contains() string method.
Wildcards are supported only by the PowerShell -like operator. The following example is just for educational purposes, I wouldn't use it in your case as .Contains() string method is simpler and faster.
If ((-split $partNo).Where{ $EiaContent -like "*$EiaContent*" }) {
Write-Host "Part Matches"
}
Note that -contains would not be suitable here. A common misconception is that -contains does a substring search, when the LHS operand is a string. It doesn't! The operator tests whether a collection (such as an array) on the LHS contains the value given on the RHS.

powershell and SQL

I am working with powershell and SQL using a query to extract drive information from a server
I am writing following query
set #sql = 'C:\WINDOWS\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -c "Get-WmiObject -Class Win32_Volume -Filter ''DriveType = 3'' | select name,label,capacity,freespace | foreach{$_.name+''!''+$_.label+''|''+$_.capacity/1048576+''%''+$_.freespace/1048576+''*''}"'
xp_cmdshell #SQL
i get following output
output
*******************************************************************************************
C:\!RISCDCC36N03C$|139980.3984375%35242.921875*
D:\!RISCDCC36N03D$|139977.99609375%34774.08984375*
G:\!RISCDCSQL552G|92151.9375%46329.1875*
M:\!|0%0*
M:\RISCDCSQL557BMP\!RISCDCSQL557BMP|81911.9375%31869.3125*
M:\RISCDCSQL557DMP\!RISCDCSQL557DMP|40954.9375%37753.5*
M:\RISCDCSQL557CMP\!RISCDCSQL557CMP|20475.9375%7643.375*
T:\!RISCDCSQL563T$|81911.9375%15462*
R:\!RISCDCSQL561R$|35836.9375%19392.0625*
P:\RISCDCSQL560BMP\!RISCDCSQL560BMP|225278.9375%15844.625*
P:\!RISCDCSQL560P$|245759.9375%13014.75*
P:\RISCDCSQL560CMP\!RISCDCSQL560CMP|122876.9375%29950.9375*
P:\RISCDCSQL560AMP\!RISCDCSQL560AMP|102398.9375%100423.25*
L:\!RISCDCSQL556L$|20479.9375%5072.1875*
I:\!RISCDCSQL553I$|512003.9375%81162.5*
I:\RISCDCSQL553MP1\!RISCDCSQL553MP1|307200.9375%137322.9375*
X:\RISCDCSQL567CMP\!RISCDCSQL567CMP|97288.9375%45540.125*
X:\!|0%0*
X:\RISCDCSQL567AMP\!RISCDCSQL567AMP|35841.9375%28526.125*
U:\!RISCDCSQL564U$|66552.9375%7892*
NULL
**************************************************************************
I don't understand why for M drive and X drive it is giving 0%0.
I am using this information to calculate further growth of space. while calculating it is givng me divide by zero.
( docs http://msdn.microsoft.com/en-us/library/windows/desktop/aa394515%28v=vs.85%29.aspx )
It looks like drive M and X each have a full volume/partition (no free space) amongst the others.
Remove them from the output with an extra filter clause -
-Filter ''DriveType = 3 and freespace>0''
That works for me, in the Powershell prompt...
PS C:\Windows\System32> Get-WmiObject -Class Win32_Volume -Filter 'DriveType = 3
and freespace>0' | select name,label,capacity,freespace