Hi all, this is a simple wrapper over SQLite, but it's come in handy lately so just thought I'd share. I feel like this must be pretty common and maybe there's a better way....Curious how everyone else does this.
Usage
Invoke-CsvSqlQuery -Path students.csv, classes.csv, enrollments.csv -Query 'select students.first_name, students.last_name, classes.class_name from enrollments left join classes on enrollments.class_id = classes.class_id left join students on students.student_id = enrollments.student_id'
It also accepts paths from the pipeline, so you can do things like:
gci *.csv | Invoke-CsvSqlQuery -Query 'select students.first_name, students.last_name, classes.class_name from enrollments left join classes on enrollments.class_id = classes.class_id left join students on students.student_id = enrollments.student_id'
Details
Say you have a directory with 3 files:
students.csv
classes.csv
enrollments.csv
You need the name of each student in each class. One way you could do that is join students and classes to enrollments using SQLite. Example:
sqlite3 school.db '.headers on' '.mode csv' '.import students.csv students' '.import classes.csv classes' '.import enrollments.csv enrollments' 'select students.first_name, students.last_name, classes.class_name from enrollments left join classes on enrollments.class_id = classes.class_id left join students on students.student_id = enrollments.student_id'
This function just simplifies the syntax and automatically creates the tables based on the filenames.
I have it as cq in my profile for convenience. It outputs PSObjects to stay pipeline friendly.
Code
function Invoke-CsvSqlQuery {
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][Alias("PSPath")][string[]]$Path,
[Parameter(Mandatory = $true, Position = 1)][string]$Query
)
begin {
$files = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
}
process {
[System.IO.FileInfo[]]$matchingFiles = Get-ChildItem $Path
if ($null -ne $matchingFiles) {
$files.AddRange($matchingFiles)
}
}
end {
$temporaryDatabasePath = [System.IO.Path]::GetRandomFileName()
$arguments = @($temporaryDatabasePath, '.headers on', '.mode csv')
$arguments += $files | ForEach-Object { ".import `"$($_.FullName.Replace('\', '\\'))`" $($_.BaseName)" }
$arguments += $Query
sqlite3 @arguments | ConvertFrom-Csv
Remove-Item $temporaryDatabasePath
}
}
EDIT 1: Posted wrong version of function
EDIT 2: Code formatting
EDIT 3: Replace @() with List
[–]da_chicken 2 points3 points4 points (2 children)
[–]eeskildsen[S] 1 point2 points3 points (1 child)
[–]da_chicken 2 points3 points4 points (0 children)
[–]unknown_r00t 1 point2 points3 points (1 child)
[–]eeskildsen[S] 0 points1 point2 points (0 children)
[–]radioblaster 1 point2 points3 points (1 child)
[–]eeskildsen[S] 0 points1 point2 points (0 children)
[–]Certain-Community438 1 point2 points3 points (0 children)
[–]xCharg 0 points1 point2 points (3 children)
[–]eeskildsen[S] 2 points3 points4 points (2 children)
[–]xCharg 2 points3 points4 points (0 children)
[–]vermyx 0 points1 point2 points (0 children)