SQL 表达式中非常大的 IN 子句
Very huge IN clause in SQL expression
假设我有一个产品 table 有 100 万条记录(是的,我有 100 万条产品!)。
此产品 table 有一个参考字符串字段。
有些人想要导出除某些产品之外的所有产品的 csv 文件。
我们知道要排除的产品的参考资料。它们存储在 csv 文件中。
这是我构建的查询:
SELECT ... FROM products WHERE reference NOT IN ('ref1','ref2','.....')
排除少于 100 个引用时一切正常。
如果我要排除的参考文献超过 10000 条,我该怎么办? sql 查询非常大。
我试过 mysql、postgre 和 sql 服务器。同样的问题。
谢谢
将 CSV 文件加载到 table 中,reference
作为主键。
然后使用not exists
:
SELECT ...
FROM products p
WHERE NOT EXISTS (SELECT 1
FROM csv_table c
WHERE c.reference = p.reference
);
当然,创建 CSV table 的逻辑取决于数据库。但是,任何可以使用索引的数据库都应该能够对此进行优化。
如果您可以对 table 进行更改;您可以有一个额外的列作为排除(位字段)并在需要排除时更新为 1,如果不需要则保持为 0。
不要维护 CSV 文件,而是将排除数据存储在 table 中,并在 运行 查询之前更新主 table。
那么在查询的时候,就用那一列作为筛选即可。
您可以在 SQL 服务器上使用 table 值参数。
查询如下所示:
SELECT *
FROM products AS P
LEFT JOIN @exludedProducts AS EP ON EP.reference = P.reference
WHERE P.reference IS NULL
您需要像这样声明 table 值类型(检查类型以匹配您的 table):
CREATE TYPE dbo.ProductReferenceTvp AS TABLE
(
reference VARCHAR(10) NOT NULL
)
您的 .Net 代码可能如下所示:
public void GetProducts(IEnumerable<string> excludedProducts)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" SELECT * ");
sb.AppendLine(" FROM products AS P ");
sb.AppendLine(" LEFT JOIN @exludedProducts AS EP ON EP.reference = P.reference ");
sb.AppendLine(" WHERE P.reference IS NULL ");
using (var cn = new SqlConnection(ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(sb.ToString(), cn))
{
var pExcludedProducts = GetTvp("@exludedProducts", excludedProducts.Distinct());
cmd.Parameters.Add(pExcludedProducts);
DataSet ds = new DataSet();
new SqlDataAdapter(cmd).Fill(ds);
Print(ds);
}
}
}
private SqlParameter GetTvp(string name, IEnumerable<string> excludedProducts)
{
var dt = new DataTable();
dt.Columns.Add("reference", typeof(String));
foreach (var product in excludedProducts)
{
dt.Rows.Add(product);
}
var p = new SqlParameter(name, dt);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ProductReferenceTvp";
return p;
}
假设我有一个产品 table 有 100 万条记录(是的,我有 100 万条产品!)。
此产品 table 有一个参考字符串字段。
有些人想要导出除某些产品之外的所有产品的 csv 文件。
我们知道要排除的产品的参考资料。它们存储在 csv 文件中。
这是我构建的查询:
SELECT ... FROM products WHERE reference NOT IN ('ref1','ref2','.....')
排除少于 100 个引用时一切正常。
如果我要排除的参考文献超过 10000 条,我该怎么办? sql 查询非常大。
我试过 mysql、postgre 和 sql 服务器。同样的问题。
谢谢
将 CSV 文件加载到 table 中,reference
作为主键。
然后使用not exists
:
SELECT ...
FROM products p
WHERE NOT EXISTS (SELECT 1
FROM csv_table c
WHERE c.reference = p.reference
);
当然,创建 CSV table 的逻辑取决于数据库。但是,任何可以使用索引的数据库都应该能够对此进行优化。
如果您可以对 table 进行更改;您可以有一个额外的列作为排除(位字段)并在需要排除时更新为 1,如果不需要则保持为 0。 不要维护 CSV 文件,而是将排除数据存储在 table 中,并在 运行 查询之前更新主 table。
那么在查询的时候,就用那一列作为筛选即可。
您可以在 SQL 服务器上使用 table 值参数。
查询如下所示:
SELECT *
FROM products AS P
LEFT JOIN @exludedProducts AS EP ON EP.reference = P.reference
WHERE P.reference IS NULL
您需要像这样声明 table 值类型(检查类型以匹配您的 table):
CREATE TYPE dbo.ProductReferenceTvp AS TABLE
(
reference VARCHAR(10) NOT NULL
)
您的 .Net 代码可能如下所示:
public void GetProducts(IEnumerable<string> excludedProducts)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" SELECT * ");
sb.AppendLine(" FROM products AS P ");
sb.AppendLine(" LEFT JOIN @exludedProducts AS EP ON EP.reference = P.reference ");
sb.AppendLine(" WHERE P.reference IS NULL ");
using (var cn = new SqlConnection(ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(sb.ToString(), cn))
{
var pExcludedProducts = GetTvp("@exludedProducts", excludedProducts.Distinct());
cmd.Parameters.Add(pExcludedProducts);
DataSet ds = new DataSet();
new SqlDataAdapter(cmd).Fill(ds);
Print(ds);
}
}
}
private SqlParameter GetTvp(string name, IEnumerable<string> excludedProducts)
{
var dt = new DataTable();
dt.Columns.Add("reference", typeof(String));
foreach (var product in excludedProducts)
{
dt.Rows.Add(product);
}
var p = new SqlParameter(name, dt);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ProductReferenceTvp";
return p;
}