-
Notifications
You must be signed in to change notification settings - Fork 29
/
Find-DatabaseValue.ps1
286 lines (265 loc) · 10.4 KB
/
Find-DatabaseValue.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<#
.SYNOPSIS
Searches an entire database for a field value.
.OUTPUTS
System.Management.Automation.PSCustomObject for each found row, including the #TableName,
#ColumnName, and all fields.
.FUNCTIONALITY
Database
.COMPONENT
System.Configuration
.LINK
ConvertFrom-DataRow.ps1
.LINK
Stop-ThrowError.ps1
.LINK
Invoke-Sqlcmd
.EXAMPLE
Find-DatabaseValue.ps1 FR -IncludeSchemata Sales -MaxRows 100 -ServerInstance '(localdb)\ProjectsV13' -Database AdventureWorks2016
TableName : [Sales].[SalesTerritory]
TerritoryID : 7
Name : France
CountryRegionCode : FR
Group : Europe
SalesYTD : 4772398.3078
SalesLastYear : 2396539.7601
CostYTD : 0.0000
CostLastYear : 0.0000
rowguid : bf806804-9b4c-4b07-9d19-706f2e689552
ModifiedDate : 04/30/2008 00:00:00
.EXAMPLE
Find-DatabaseValue.ps1 41636 -IncludeColumns %OrderID -ServerInstance '(localdb)\ProjectsV13' -Database AdventureWorks2016 |tee order41636.txt
TableName : [Production].[TransactionHistory]
TransactionID : 100046
ProductID : 826
ReferenceOrderID : 41636
ReferenceOrderLineID : 0
TransactionDate : 07/31/2013 00:00:00
TransactionType : W
Quantity : 4
ActualCost : 0.0000
ModifiedDate : 07/31/2013 00:00:00
TableName : [Production].[WorkOrder]
WorkOrderID : 41636
ProductID : 826
OrderQty : 4
StockedQty : 4
ScrappedQty : 0
StartDate : 07/31/2013 00:00:00
EndDate : 08/11/2013 00:00:00
DueDate : 08/11/2013 00:00:00
ScrapReasonID :
ModifiedDate : 08/11/2013 00:00:00
TableName : [Production].[WorkOrderRouting]
WorkOrderID : 41636
ProductID : 826
OperationSequence : 6
LocationID : 50
ScheduledStartDate : 07/31/2013 00:00:00
ScheduledEndDate : 08/11/2013 00:00:00
ActualStartDate : 08/01/2013 00:00:00
ActualEndDate : 08/11/2013 00:00:00
ActualResourceHrs : 3.0000
PlannedCost : 36.7500
ActualCost : 36.7500
ModifiedDate : 08/11/2013 00:00:00
#>
#Requires -Version 3
[CmdletBinding()][OutputType([Management.Automation.PSCustomObject])] Param(
<#
The value to search for. The datatype is significant, e.g. searching for money/smallmoney columns, cast the type to decimal: [decimal]13.55
Searches, by type:
* string: varchar, char, nvarchar, nchar (char length must be at least as long as value)
* byte: tinyint
* int: bigint, int
* long: bigint, numeric or decimal (where scale is zero)
* decimal: money, smallmoney
* double or float: float, real, numeric, decimal
* datetime: date (if no time specified), datetime, datetime2, datetimeoffset, smalldatetime
* timespan: time
If the -LikeValue switch is specified, the type of value is assumed to be string.
#>
[Parameter(Position=0,Mandatory=$true)] $Value,
# The server and instance to connect to.
[Parameter(ParameterSetName='ByConnectionParameters',Mandatory=$true)][string] $ServerInstance,
# The database to use.
[Parameter(ParameterSetName='ByConnectionParameters',Mandatory=$true)][string] $Database,
# Specifies a connection string to connect to the server.
[Parameter(ParameterSetName='ByConnectionString',Mandatory=$true)][Alias('ConnStr','CS')][string] $ConnectionString,
# Specifies an SMO Database object to query.
[Parameter(ParameterSetName='ByDatabase',Mandatory=$true)]
[Microsoft.SqlServer.Management.Smo.Database] $SmoDatabase,
# The connection string name from the ConfigurationManager to use.
[Parameter(ParameterSetName='ByConnectionName',Mandatory=$true)][string] $ConnectionName,
# A like-pattern of database schemata to include (will only include these).
[string[]] $IncludeSchemata,
# A like-pattern of database schemata to exclude.
[string[]] $ExcludeSchemata,
# A like-pattern of database tables to include (will only include these).
[string[]] $IncludeTables,
# A like-pattern of database tables to exclude.
[string[]] $ExcludeTables,
# A like-pattern of database columns to include (will only include these).
[string[]] $IncludeColumns,
# A like-pattern of database columns to exclude.
[string[]] $ExcludeColumns,
# Tables with more rows than this value will be skipped.
[int] $MinRows = 1,
# Tables with more rows than this value will be skipped.
[int] $MaxRows,
# Quit as soon as the first value is found.
[switch] $FindFirst,
# Interpret the value as a like-pattern (% for zero-or-more characters, _ for a single character, \ is escape).
[switch] $LikeValue
)
try{[void][Configuration.ConfigurationManager]}catch{Add-Type -AssemblyName System.Configuration}
function Format-LikeCondition([string]$column,[string[]]$patterns,[switch]$not)
{
$like,$andOr = if($not){'not like','and'}else{'like','or'}
@"
and ( $(($patterns |ForEach-Object {"$column $like '$($_ -replace '''','''''')' escape '\'"}) -join " $andOr ") )
"@
}
Use-SqlcmdParams.ps1 -QueryTimeout 300
if($Value -is [int])
{
if($Value -le [byte]::MaxValue) {$Value = [byte] $Value}
elseif($Value -le [short]::MaxValue) {$Value = [short] $Value}
}
$selectFrom = "select '{0}.{1}' [#TableName], '{2}' [#ColumnName], * from"
$colssql = @"
select quotename(TABLE_SCHEMA) TABLE_SCHEMA,
quotename(TABLE_NAME) TABLE_NAME,
quotename(COLUMN_NAME) COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
"@
if($LikeValue)
{
$minLength = ($Value -replace '\\.','_' -replace '%','').Length
Write-Verbose "Searching for character data with a minimum length of $minLength to match pattern."
$colssql += @"
where DATA_TYPE in ('varchar','char','nvarchar','nchar')
and (CHARACTER_MAXIMUM_LENGTH = -1 or CHARACTER_MAXIMUM_LENGTH >= $minLength)
"@
$valsql = "$selectFrom {0}.{1} where {2} like '$($Value -replace '''','''''')' escape '\';"
}
elseif($Value -is [string])
{
Write-Verbose "Searching for character data with a minimum length of $($Value.Length)."
$colssql += @"
where DATA_TYPE in ('varchar','char','nvarchar','nchar')
and (CHARACTER_MAXIMUM_LENGTH = -1 or CHARACTER_MAXIMUM_LENGTH >= $($Value.Length))
"@
$valsql = "$selectFrom {0}.{1} where {2} = '$($Value -replace '''','''''')';"
}
elseif($Value -is [byte])
{
Write-Verbose "Searching for byte (tinyint) data."
$colssql += @"
where DATA_TYPE in ('tinyint','smallint','int')
"@
$valsql = "$selectFrom {0}.{1} where {2} = $Value;"
}
elseif($Value -is [short])
{
Write-Verbose "Searching for short (smallint) data."
$colssql += @"
where DATA_TYPE in ('smallint','int')
"@
$valsql = "$selectFrom {0}.{1} where {2} = $Value;"
}
elseif($Value -is [int])
{
Write-Verbose "Searching for integer data."
$colssql += @"
where DATA_TYPE in ('int')
"@
$valsql = "$selectFrom {0}.{1} where {2} = $Value;"
}
elseif($Value -is [long])
{
Write-Verbose "Searching for long integer data."
$colssql += @"
where (DATA_TYPE = 'bigint'
or (DATA_TYPE in ('numeric','decimal') and NUMERIC_SCALE = 0))
"@
$valsql = "$selectFrom {0}.{1} where {2} = '$Value';"
}
elseif($Value -is [decimal])
{
Write-Verbose "Searching for decimal (money) data."
$colssql += @"
where DATA_TYPE in ('money','smallmoney')
"@
$valsql = "$selectFrom {0}.{1} where {2} = $Value;"
}
elseif($Value -is [double] -or $Value -is [float])
{
Write-Verbose "Searching for double-precision floating-point or money data."
$colssql += @"
where DATA_TYPE in ('float','real','numeric','decimal')
"@
$valsql = "$selectFrom {0}.{1} where {2} = $Value;"
}
elseif($Value -is [datetime] -and $Value.TimeOfDay -eq 0)
{
Write-Verbose "Searching for date data."
$colssql += @"
where DATA_TYPE in ('date','datetime','datetime2','datetimeoffset','smalldatetime')
"@
$valsql = "$selectFrom {0}.{1} where {2} = '$($Value.ToString('yyyy-MM-dd'))';"
}
elseif($Value -is [datetime])
{
Write-Verbose "Searching for datetime data."
$colssql += @"
where DATA_TYPE in ('datetime','datetime2','datetimeoffset','smalldatetime')
"@
$valsql = "$selectFrom {0}.{1} where {2} = '$($Value.ToString('u'))';"
}
elseif($Value -is [timespan])
{
Write-Verbose "Searching for time data."
$colssql += @"
where DATA_TYPE in ('time')
"@
$valsql = "$selectFrom {0}.{1} where {2} = '$($Value.ToString('HH:mm:ss.fffff'))';"
}
if($IncludeSchemata) { $colssql += Format-LikeCondition TABLE_SCHEMA $IncludeSchemata }
if($ExcludeSchemata) { $colssql += Format-LikeCondition TABLE_SCHEMA $ExcludeSchemata -Not }
if($IncludeTables) { $colssql += Format-LikeCondition TABLE_NAME $IncludeTables }
if($ExcludeTables) { $colssql += Format-LikeCondition TABLE_NAME $ExcludeTables -Not }
if($IncludeColumns) { $colssql += Format-LikeCondition COLUMN_NAME $IncludeColumns }
if($ExcludeColumns) { $colssql += Format-LikeCondition COLUMN_NAME $ExcludeColumns -Not }
$colssql += ' order by TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION;'
Write-Debug "Schema Query:`n$colssql"
$corpus = Invoke-Sqlcmd $colssql |ConvertFrom-DataRow.ps1
if(!$corpus) {Stop-ThrowError.ps1 'No columns left to search.' -SearchContext $PSBoundParameters}
Write-Verbose "Searching $($corpus.Length) tables"
$count,$p,$rows,$lasttable = 0,0,0,''
foreach($row in $corpus)
{
Import-Variables.ps1 $row
if($lasttable -ne "$TABLE_SCHEMA.$TABLE_NAME")
{
[int]$rows = Invoke-Sqlcmd "select count(*) rows from $TABLE_SCHEMA.$TABLE_NAME" |ConvertFrom-DataRow.ps1 -AsValues
$lasttable = "$TABLE_SCHEMA.$TABLE_NAME"
}
Write-Progress 'Searching columns' "$TABLE_SCHEMA.$TABLE_NAME.$COLUMN_NAME" 1 -CurrentOperation "$rows rows" `
-PercentComplete ((++$p)*100/$corpus.Length) -ErrorAction Ignore
if($rows -lt $MinRows) {Write-Verbose "Skipping $TABLE_SCHEMA.$TABLE_NAME ($rows rows < $MinRows)"; continue}
if($MaxRows -and $rows -gt $MaxRows) {Write-Verbose "Skipping $TABLE_SCHEMA.$TABLE_NAME ($rows rows > $MaxRows)"; continue}
$query = $valsql -f $TABLE_SCHEMA,$TABLE_NAME,$COLUMN_NAME
[Data.DataTable]$data = $null
Write-Verbose "Query: $query"
$data = try {Invoke-Sqlcmd $query -OutputAs DataTables} catch {Write-Error $_; continue}
if($data -and ($data.Rows.Count -gt 0))
{
$count += $data.Rows.Count
Write-Verbose "Found $($data.Rows.Count) rows in $TABLE_SCHEMA.$TABLE_NAME."
$data.Rows |ConvertFrom-DataRow.ps1
if($FindFirst) { break }
}
}
if(!$count) {Write-Warning "No rows found."}
else {Write-Verbose "Found $count total rows."}