在 USQL 中处理具有不同列的文件
Handling Files With Different Columns in USQL
我有一个 USQL 脚本和 CSV 提取器来加载我的文件。然而,有些月份文件可能包含 4 列,有些月份可能包含 5 列。
如果我使用包含 4 个或 5 个字段的列列表设置提取器,我会收到有关文件预期宽度的错误消息。去检查定界符等等。不足为奇。
考虑到 USQL 仍然是新手并且缺少一些基本的错误处理,请问有什么解决这个问题的方法?
我尝试在提取器中使用 silent 子句来忽略更宽的列,这对于 4 列很方便。然后使用 IF 条件获取行集的行数,然后有一个 5 列的提取器。然而,这会导致大量行集变量未在 IF 表达式中用作标量变量。
我还尝试了 C# 样式计数和 sizeof(@AttemptExtractWith4Cols)。都不行。
让您感受一下我所采用的方法的代码片段:
DECLARE @SomeFilePath string = @"/MonthlyFile.csv";
@AttemptExtractWith4Cols =
EXTRACT Col1 string,
Col2 string,
Col3 string,
Col4 string
FROM @SomeFilePath
USING Extractors.Csv(silent : true); //can't be good.
//can't assign rowset to scalar variable!
DECLARE @RowSetCount int = (SELECT COUNT(*) FROM @AttemptExtractWith4Cols);
//tells me @AttemptExtractWith4Cols doesn't exist in the current context!
DECLARE @RowSetCount int = @AttemptExtractWith4Cols.Count();
IF (@RowSetCount == 0) THEN
@AttemptExtractWith5Cols =
EXTRACT Col1 string,
Col2 string,
Col3 string,
Col4 string,
Col5 string
FROM @SomeFilePath
USING Extractors.Csv(); //not silent
END;
//etc
当然,如果 USQL 中有 TRY CATCH 块这样的东西,这会容易得多。
这是否是一个合理的方法?
如有任何意见,我们将不胜感激。
感谢您的宝贵时间。
U-SQL 现在支持 OUTER UNION 所以你可以这样处理:
// Scenario 1; file has 4 columns
DECLARE @file1 string = @"/input/file1.csv";
// Scenario 2; file has 5 columns
//DECLARE @file1 string = @"/input/file2.csv";
@file =
EXTRACT col1 string,
col2 string,
col3 string,
col4 string
FROM @file1
USING Extractors.Csv(silent : true)
OUTER UNION ALL BY NAME ON (col1, col2, col3, col4)
EXTRACT col1 string,
col2 string,
col3 string,
col4 string,
col5 string
FROM @file1
USING Extractors.Csv(silent : true);
@output =
SELECT *
FROM @file;
OUTPUT @output
TO "/output/output.csv"
USING Outputters.Csv();
在我的示例中,文件 1 有 4 列,文件 2 有 5 列。脚本在任何一种情况下都能成功运行。
我的结果:
希望这是有道理的。
外部联合是一个很好的解决方案。或者,如果您希望文件中的行不同,您也可以编写自己的通用提取器。有关示例,请参阅 this blog post。
这是我发现有帮助的另一个解决方案。您可以将文件作为单个文本列读取(使用“\t”作为分隔符,因为没有任何分隔符),然后使用 C# 字符串函数动态拆分。我已经在类似的问题上对此进行了测试。这种方法的优点是您可以对任意数量的列使用相同的方法。
SELECT
(String)(ColList[0]) AS ColA
, (String)(ColList[1]) AS ColB
, (String)(ColList[2]) AS ColC
, (String)(ColList[3]) AS ColD
, (int?)(NumColumns >= 5 ? (String)(ColList[4]) : (String)null)
AS ColE
FROM (
SELECT ColList
, ColList.Count AS NumColumns
FROM (
SELECT SqlArray.Create(RowText.Split(',')) AS ColList
FROM (
EXTRACT RowText string
FROM @SomeFilePath
USING Extractors.Text(delimiter: '\t', quoting: false)
) AS [T1]
) AS [T2]
) AS [T3]
警告:此解决方案无法识别文本引用。字段值中的任何逗号都会破坏此逻辑。
我有一个 USQL 脚本和 CSV 提取器来加载我的文件。然而,有些月份文件可能包含 4 列,有些月份可能包含 5 列。
如果我使用包含 4 个或 5 个字段的列列表设置提取器,我会收到有关文件预期宽度的错误消息。去检查定界符等等。不足为奇。
考虑到 USQL 仍然是新手并且缺少一些基本的错误处理,请问有什么解决这个问题的方法?
我尝试在提取器中使用 silent 子句来忽略更宽的列,这对于 4 列很方便。然后使用 IF 条件获取行集的行数,然后有一个 5 列的提取器。然而,这会导致大量行集变量未在 IF 表达式中用作标量变量。
我还尝试了 C# 样式计数和 sizeof(@AttemptExtractWith4Cols)。都不行。
让您感受一下我所采用的方法的代码片段:
DECLARE @SomeFilePath string = @"/MonthlyFile.csv";
@AttemptExtractWith4Cols =
EXTRACT Col1 string,
Col2 string,
Col3 string,
Col4 string
FROM @SomeFilePath
USING Extractors.Csv(silent : true); //can't be good.
//can't assign rowset to scalar variable!
DECLARE @RowSetCount int = (SELECT COUNT(*) FROM @AttemptExtractWith4Cols);
//tells me @AttemptExtractWith4Cols doesn't exist in the current context!
DECLARE @RowSetCount int = @AttemptExtractWith4Cols.Count();
IF (@RowSetCount == 0) THEN
@AttemptExtractWith5Cols =
EXTRACT Col1 string,
Col2 string,
Col3 string,
Col4 string,
Col5 string
FROM @SomeFilePath
USING Extractors.Csv(); //not silent
END;
//etc
当然,如果 USQL 中有 TRY CATCH 块这样的东西,这会容易得多。
这是否是一个合理的方法?
如有任何意见,我们将不胜感激。
感谢您的宝贵时间。
U-SQL 现在支持 OUTER UNION 所以你可以这样处理:
// Scenario 1; file has 4 columns
DECLARE @file1 string = @"/input/file1.csv";
// Scenario 2; file has 5 columns
//DECLARE @file1 string = @"/input/file2.csv";
@file =
EXTRACT col1 string,
col2 string,
col3 string,
col4 string
FROM @file1
USING Extractors.Csv(silent : true)
OUTER UNION ALL BY NAME ON (col1, col2, col3, col4)
EXTRACT col1 string,
col2 string,
col3 string,
col4 string,
col5 string
FROM @file1
USING Extractors.Csv(silent : true);
@output =
SELECT *
FROM @file;
OUTPUT @output
TO "/output/output.csv"
USING Outputters.Csv();
在我的示例中,文件 1 有 4 列,文件 2 有 5 列。脚本在任何一种情况下都能成功运行。
我的结果:
希望这是有道理的。
外部联合是一个很好的解决方案。或者,如果您希望文件中的行不同,您也可以编写自己的通用提取器。有关示例,请参阅 this blog post。
这是我发现有帮助的另一个解决方案。您可以将文件作为单个文本列读取(使用“\t”作为分隔符,因为没有任何分隔符),然后使用 C# 字符串函数动态拆分。我已经在类似的问题上对此进行了测试。这种方法的优点是您可以对任意数量的列使用相同的方法。
SELECT
(String)(ColList[0]) AS ColA
, (String)(ColList[1]) AS ColB
, (String)(ColList[2]) AS ColC
, (String)(ColList[3]) AS ColD
, (int?)(NumColumns >= 5 ? (String)(ColList[4]) : (String)null)
AS ColE
FROM (
SELECT ColList
, ColList.Count AS NumColumns
FROM (
SELECT SqlArray.Create(RowText.Split(',')) AS ColList
FROM (
EXTRACT RowText string
FROM @SomeFilePath
USING Extractors.Text(delimiter: '\t', quoting: false)
) AS [T1]
) AS [T2]
) AS [T3]
警告:此解决方案无法识别文本引用。字段值中的任何逗号都会破坏此逻辑。