LINQ to SQL是否可以搜索整个数据库(显然只有.dbml文件中映射的部分)才能匹配字符串?我正在尝试编写一个函数,它将采用一串“搜索术语”并搜索所有映射的实体并返回一个List(Of Object),它可以包含实体的混合,即如果我有一个表“Foo”和表“ Bar“并搜索”wibble“,如果”Foo“中有一行,而”Bar“中有一行包含”wibble“,我想返回一个包含”Foo“对象的List(Of Object)和一个”酒吧“对象。 这可能吗?
答案 0 :(得分:8)
向你的老板询问以下内容:
“老板,当你去图书馆找一本关于小工具的书时,你是否走到第一个书架并开始阅读每本书以查看它是否相关,或者你是否使用某种预编译索引那个图书管理员已经提前为你配置了什么?“
如果他说“嗯,我会使用索引”,那么你需要一个全文索引。
如果他说“嗯,我会逐一阅读每本书”,那么你需要一份新工作,一位新老板,或两者兼而有之: - )
答案 1 :(得分:7)
LINQ to SQL,一般来说,ORM甚至SQL都是这种查询的不匹配。您正在描述全文搜索,因此您应该使用SQL Server的全文搜索功能。自2000年以来,所有版本和版本都提供Full Text Search,包括SQL Server Express。您需要创建一个FTS目录并编写在查询中使用CONTAINS,FREETEXT函数的查询。
为什么需要这样的功能?除非您特别想要为您的应用程序启用FTS,否则这是一种......奇怪的...访问您的数据的方式。
答案 2 :(得分:3)
这可能是“可能的”,但大多数数据库都是通过网络或网络访问的,因此这是一项非常昂贵的操作。所以这听起来很糟糕。
还有表名和列名的问题,这可能是你最大的问题。可以通过反射获取列名,但我不知道表名:
foreach (PropertyInfo property in typeof(TEntity).GetProperties())
yield return property.Name;
编辑:@Ben,你说错了。
答案 3 :(得分:2)
这可以做但不会很漂亮。有几种可能的解决方案。
1。自己编写每个表的查询,并在查询方法中执行所有查询。
var users = context.Users
.Where(x => x.FirstName.Contains(txt) || x.LastName.Contains(txt))
.ToList();
var products = context.Products
.Where(x => x.ProductName.Contains(txt));
var result = user.Cast<Object>().Concat(products.Cast<Object>());
2. 将所有(相关)表提取到内存中并使用反射执行搜索。编写的代码越少,就会产生巨大的性能影响。
3. 使用反射为搜索构建表达式树。这可能是最好的解决方案,但实现起来可能具有挑战性。
4. 使用专为全文搜索设计的内容 - 例如集成到SQL Server或Apache Lucene中的全文搜索。
所有LINQ解决方案(可能)每个表需要一个查询,如果您有许多表,则会产生不可忽视的性能影响。在这里,人们应该寻找一种解决方案,将这些查询批量分成一个。我们使用LINQ to SQL的一个项目使用了一个库进行批处理查询,但我不知道它的名称是什么以及它究竟能做什么,因为我大部分时间都在前端团队工作。
答案 4 :(得分:0)
可能,但从我的观点来看,不建议这样做。考虑拥有100个表的1000K记录。 性能低下您可以通过在数据库级别创建Sp并通过实体调用来实现 Linq to SQL 。它会比你试图达到的速度快得多=)
答案 5 :(得分:0)
迟到的回答,但由于我不得不为自己想出一些东西,这里就是这样。我写了以下内容来搜索所有表的所有列以进行字符串匹配。这与数据取证任务有关,该任务是在重量大约为24GB的数据库中查找字符串匹配的所有出现的。在这个大小,你可以想象使用游标或单线程查询会相当慢,搜索整个数据库需要很长时间。我编写了以下CLR存储过程来为我的服务器端工作并以XML格式返回结果,同时强制并行化。它的速度非常快。标准AdventureWorks2017数据库的数据库范围搜索在不到2秒的时间内完成。享受!
示例用法:
使用服务器上的所有可用处理器:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael'
将服务器限制为4个并发线程:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @maxDegreeOfParallelism = 4
在搜索字词中使用逻辑运算符:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = '(john or michael) and not jack', @tablesSearchTerm = 'not contact'
将搜索限制为包含某些搜索字词的表名和/或列名:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @tablesSearchTerm = 'person contact', @columnsSearchTerm = 'address name'
将搜索结果限制在找到术语的每个表的第一行:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @getOnlyFirstRowPerTable = 1
仅将搜索限制为模式只会自动返回每个表的第一行:
EXEC [dbo].[SearchAllTables] @tablesSearchTerm = 'person contact'
仅返回搜索查询:
EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @tablesSearchTerm = 'person contact', @onlyOutputQueries = 1
将结果捕获到临时表和排序中:
CREATE TABLE #temp (Result NVARCHAR(MAX)); INSERT INTO #temp EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john'; SELECT * FROM #temp ORDER BY Result ASC; DROP TABLE #temp;
答案 6 :(得分:0)
我最终写了这个小的自定义宝石(给了搜索词找到所有匹配的记录):
namespace SqlServerMetaSearchScan
{
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Xml;
public class Program
{
#region Ignition
public static void Main(string[] args)
{
// Defaulting
SqlConnection connection = null;
try
{
// Questions
ColorConsole.Print("SQL Connection String> ");
string connectionString = Console.ReadLine();
ColorConsole.Print("Search Term (Case Ignored)> ");
string searchTerm = Console.ReadLine();
ColorConsole.Print("Skip Databases (Comma Delimited)> ");
List<string> skipDatabases = Console.ReadLine().Split(',').Where(item => item.Trim() != string.Empty).ToList();
// Search
connection = new SqlConnection(connectionString);
connection.Open();
// Each database
List<string> databases = new List<string>();
string databasesLookup = "SELECT name FROM master.dbo.sysdatabases";
SqlDataReader reader = new SqlCommand(databasesLookup, connection).ExecuteReader();
while (reader.Read())
{
// Capture
databases.Add(reader.GetValue(0).ToString());
}
// Build quintessential folder
string logsDirectory = @"E:\Logs";
if (!Directory.Exists(logsDirectory))
{
// Build
Directory.CreateDirectory(logsDirectory);
}
string baseFolder = @"E:\Logs\SqlMetaProbeResults";
if (!Directory.Exists(baseFolder))
{
// Build
Directory.CreateDirectory(baseFolder);
}
// Close reader
reader.Close();
// Sort databases
databases.Sort();
// New space
Console.WriteLine(Environment.NewLine + " Found " + databases.Count + " Database(s) to Scan" + Environment.NewLine);
// Deep scan
foreach (string databaseName in databases)
{
// Skip skip databases
if (skipDatabases.Contains(databaseName))
{
// Skip
continue;
}
// Select the database
new SqlCommand("USE " + databaseName, connection).ExecuteNonQuery();
// Table count
int tablePosition = 1;
try
{
// Defaulting
List<string> tableNames = new List<string>();
// Schema examination
DataTable table = connection.GetSchema("Tables");
// Query tables
string tablesLookup = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
using (SqlDataReader databaseReader = new SqlCommand(tablesLookup, connection).ExecuteReader())
{
// Get data
while (databaseReader.Read())
{
// Push
if (databaseReader.GetValue(0).ToString().Trim() != string.Empty)
{
tableNames.Add(databaseReader.GetValue(0).ToString());
}
}
// Bail
databaseReader.Close();
}
// Sort
tableNames.Sort();
// Cycle tables
foreach (string tableName in tableNames)
{
// Build data housing
string databasePathName = @"E:\Logs\\SqlMetaProbeResults" + databaseName;
string tableDirectoryPath = @"E:\Logs\SqlMetaProbeResults\" + databaseName + @"\" + tableName;
// Count first
int totalEntityCount = 0;
int currentEntityPosition = 0;
string countQuery = "SELECT count(*) FROM " + databaseName + ".dbo." + tableName;
using (SqlDataReader entityCountReader = new SqlCommand(countQuery, connection).ExecuteReader())
{
// Query count
while (entityCountReader.Read())
{
// Capture
totalEntityCount = int.Parse(entityCountReader.GetValue(0).ToString());
}
// Close
entityCountReader.Close();
}
// Write the objects into the houseing
string jsonLookupQuery = "SELECT * FROM " + databaseName + ".dbo." + tableName;
using (SqlDataReader tableReader = new SqlCommand(jsonLookupQuery, connection).ExecuteReader())
{
// Defaulting
List<string> fieldValueListing = new List<string>();
// Read continue
while (tableReader.Read())
{
// Increment
currentEntityPosition++;
// Defaulting
string identity = null;
// Gather data
for (int i = 0; i < tableReader.FieldCount; i++)
{
// Set
if (tableReader.GetName(i).ToUpper() == "ID")
{
identity = tableReader.GetValue(0).ToString();
}
else
{
// Build column data entry
string thisColumn = tableReader.GetValue(i) != null ? "'" + tableReader.GetValue(i).ToString().Trim() + "'" : string.Empty;
// Piece
fieldValueListing.Add(thisColumn);
}
}
// Path-centric
string explicitIdentity = identity ?? Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();
string filePath = tableDirectoryPath + @"\" + "Obj." + explicitIdentity + ".json";
string reStringed = JsonConvert.SerializeObject(fieldValueListing, Newtonsoft.Json.Formatting.Indented);
string percentageMark = ((double)tablePosition / (double)tableNames.Count * 100).ToString("#00.0") + "%";
string thisMarker = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();
string entityPercentMark = string.Empty;
if (totalEntityCount != 0 && currentEntityPosition != 0)
{
// Percent mark
entityPercentMark = ((double)currentEntityPosition / (double)totalEntityCount * 100).ToString("#00.0") + "%";
}
// Search term verify
if (searchTerm.Trim() != string.Empty)
{
// Search term scenario
if (reStringed.ToLower().Trim().Contains(searchTerm.ToLower().Trim()))
{
// Lazy build
if (!Directory.Exists(tableDirectoryPath))
{
// Build
Directory.CreateDirectory(tableDirectoryPath);
}
// Has the term
string idMolding = identity == null || identity == string.Empty ? "No Identity" : identity;
File.WriteAllText(filePath, reStringed);
ColorConsole.Print(percentageMark + " => " + databaseName + "." + tableName + "." + idMolding + "." + thisMarker + " (" + entityPercentMark + ")", ConsoleColor.Green, ConsoleColor.Black, true);
}
else
{
// Show progress
string idMolding = identity == null || identity == string.Empty ? "No Identity" : identity;
ColorConsole.Print(percentageMark + " => " + databaseName + "." + tableName + "." + idMolding + "." + thisMarker + " (" + entityPercentMark + ")", ConsoleColor.Yellow, ConsoleColor.Black, true);
}
}
}
// Close
tableReader.Close();
}
// Increment
tablePosition++;
}
}
catch (Exception err)
{
ColorConsole.Print("DB.Tables!: " + err.Message, ConsoleColor.Red, ConsoleColor.White, false);
}
}
}
catch (Exception err)
{
ColorConsole.Print("KABOOM!: " + err.ToString(), ConsoleColor.Red, ConsoleColor.White, false);
}
finally
{
try { connection.Close(); }
catch { }
}
// Await
ColorConsole.Print("Done.");
Console.ReadLine();
}
#endregion
#region Cores
public static string GenerateHash(string inputString)
{
// Defaulting
string calculatedChecksum = null;
// Calculate
SHA256Managed checksumBuilder = new SHA256Managed();
string hashString = string.Empty;
byte[] hashBytes = checksumBuilder.ComputeHash(Encoding.ASCII.GetBytes(inputString));
foreach (byte theByte in hashBytes)
{
hashString += theByte.ToString("x2");
}
calculatedChecksum = hashString;
// Return
return calculatedChecksum;
}
#endregion
#region Colors
public class ColorConsole
{
#region Defaulting
public static ConsoleColor DefaultBackground = ConsoleColor.DarkBlue;
public static ConsoleColor DefaultForeground = ConsoleColor.Yellow;
public static string DefaultBackPorch = " ";
#endregion
#region Printer Cores
public static void Print(string phrase)
{
// Use primary
Print(phrase, DefaultForeground, DefaultBackground, false);
}
public static void Print(string phrase, ConsoleColor customForecolor)
{
// Use primary
Print(phrase, customForecolor, DefaultBackground, false);
}
public static void Print(string phrase, ConsoleColor customBackcolor, bool inPlace)
{
// Use primary
Print(phrase, DefaultForeground, customBackcolor, inPlace);
}
public static void Print(string phrase, ConsoleColor customForecolor, ConsoleColor customBackcolor)
{
// Use primary
Print(phrase, customForecolor, customBackcolor, false);
}
public static void Print(string phrase, ConsoleColor customForecolor, ConsoleColor customBackcolor, bool inPlace)
{
// Capture settings
ConsoleColor captureForeground = Console.ForegroundColor;
ConsoleColor captureBackground = Console.BackgroundColor;
// Change colors
Console.ForegroundColor = customForecolor;
Console.BackgroundColor = customBackcolor;
// Write
if (inPlace)
{
// From beginning of this line + padding
Console.Write("\r" + phrase + DefaultBackPorch);
}
else
{
// Normal write
Console.Write(phrase);
}
// Revert
Console.ForegroundColor = captureForeground;
Console.BackgroundColor = captureBackground;
}
#endregion
}
#endregion
}
}