Доступ к файлам, доступным через SparkContext.addFile
(и --files
), можно получить через SparkFiles
. Он предоставляет два метода:
getDirectory()
- возвращает корневой каталог для распределенных файлов get(filename)
- возвращает абсолютный путь к файлу Я не уверен, существуют ли какие-то ограничения Dataproc, но что-то вроде этого должно работать нормально:
from pyspark import SparkFiles
with open(SparkFiles.get('test.yml')) as test_file:
logging.info(test_file.read())
Использование Exception
s для управляющей логики, как и в некоторых других ответах, считается плохой практикой и имеет издержки на производительность.
Проникновение по полям может привести к небольшому результату, если вы используете его много, и вы может хотеть рассмотреть кеширование результатов
. Более подходящий способ сделать это:
public static class DataRecordExtensions
{
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i=0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
Гораздо лучше использовать эту логическую функцию:
r.GetSchemaTable().Columns.Contains(field)
Один вызов - никаких исключений. Это может вызвать исключения изнутри, но я так не думаю. ПРИМЕЧАНИЕ. В комментариях ниже мы поняли это ... правильный код на самом деле:
public static bool HasColumn(DbDataReader Reader, string ColumnName) {
foreach (DataRow row in Reader.GetSchemaTable().Rows) {
if (row["ColumnName"].ToString() == ColumnName)
return true;
} //Still here? Column not found.
return false;
}
Ниже приведен пример linq версии принятого ответа:
Enumerable.Range(0, reader.FieldCount).Any(i => reader.GetName(i) == "COLUMN_NAME_GOES_HERE")
Я думаю, что лучше всего называть GetOrdinal ("columnName") на вашем DataReader спереди и уловить исключение IndexOutOfRangeException, если столбец отсутствует.
На самом деле, давайте сделаем метод расширения:
public static bool HasColumn(this IDataRecord r, string columnName)
{
try
{
return r.GetOrdinal(columnName) >= 0;
}
catch (IndexOutOfRangeException)
{
return false;
}
}
Edit
Хорошо, этот пост начинает собирать несколько проголосовавших в последнее время, и я не могу удалить его, потому что это принятый ответ, поэтому я собираюсь обновить его и (надеюсь) попытаться оправдать использование обработки исключений в качестве потока управления.
Другой способ достижения этого, поскольку Chad Grant , состоит в том, чтобы перебирать каждое поле в DataReader и проводить сравнение без учета регистра для имени поля, которое вы ищете. Это будет работать очень хорошо, и, по правде говоря, он будет работать лучше, чем мой метод выше. Конечно, я бы никогда не использовал метод выше внутри цикла, где performace была проблемой.
Я могу думать об одной ситуации, в которой метод try / GetOrdinal / catch будет работать там, где этого цикла нет. Это сейчас, однако, совершенно гипотетическая ситуация, так что это очень надуманное оправдание. Независимо от того, медведь со мной и посмотреть, что вы думаете.
Представьте базу данных, которая позволила вам столбцы «псевдоним» в таблице. Представьте, что я мог бы определить таблицу с столбцом «EmployeeName», но также дать ей псевдоним «EmpName», а выбор для любого имени вернет данные в этом столбце.
Теперь представьте, что для этой базы данных есть поставщик ADO.NET, и они закодировали для него реализацию IDataReader, которая учитывает псевдонимы столбцов.
Теперь dr.GetName(i)
(как используется в ответе Чада) может возвращать только одну строку, поэтому она должна возвращать только one из «псевдонимов» в столбце. Тем не менее, GetOrdinal("EmpName")
может использовать внутреннюю реализацию полей этого провайдера для проверки псевдонима каждого столбца для имени, которое вы ищете.
В этой гипотетической ситуации с «псевдонимом столбцов» try / GetOrdinal / catch метод был бы единственным способом убедиться в том, что вы проверяете каждое изменение имени столбца в наборе результатов.
Flimsy? Конечно. Но стоит подумать. Честно говоря, я бы предпочел «официальный» метод HasColumn в IDataRecord.
Вы также можете вызвать GetSchemaTable () на вашем DataReader, если вам нужен список столбцов, и вы не хотите получать исключение ...
Я также не работал GetSchemaTable
, пока не нашел таким образом .
В основном я это делаю:
Dim myView As DataView = dr.GetSchemaTable().DefaultView
myView.RowFilter = "ColumnName = 'ColumnToBeChecked'"
If myView.Count > 0 AndAlso dr.GetOrdinal("ColumnToBeChecked") <> -1 Then
obj.ColumnToBeChecked = ColumnFromDb(dr, "ColumnToBeChecked")
End If
Несмотря на отсутствие открытого метода, во внутреннем классе System.Data.ProviderBase.FieldNameLookup
, который полагается SqlDataReader
, существует метод.
Чтобы получить доступ к нему и получить собственную производительность, вы должны использовать ILGenerator для создания метода во время выполнения. Следующий код предоставит вам прямой доступ к int IndexOf(string fieldName)
в классе System.Data.ProviderBase.FieldNameLookup
, а также выполнит книгу, сохраняя при этом, что SqlDataReader.GetOrdinal()
делает так, что побочный эффект отсутствует. Сгенерированный код отражает существующие SqlDataReader.GetOrdinal()
, за исключением того, что он вызывает FieldNameLookup.IndexOf()
вместо FieldNameLookup.GetOrdinal()
. Метод GetOrdinal()
вызывает функцию IndexOf()
и выдает исключение, если возвращается -1
, поэтому мы обходим это поведение.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Reflection.Emit;
public static class SqlDataReaderExtensions {
private delegate int IndexOfDelegate(SqlDataReader reader, string name);
private static IndexOfDelegate IndexOf;
public static int GetColumnIndex(this SqlDataReader reader, string name) {
return name == null ? -1 : IndexOf(reader, name);
}
public static bool ContainsColumn(this SqlDataReader reader, string name) {
return name != null && IndexOf(reader, name) >= 0;
}
static SqlDataReaderExtensions() {
Type typeSqlDataReader = typeof(SqlDataReader);
Type typeSqlStatistics = typeSqlDataReader.Assembly.GetType("System.Data.SqlClient.SqlStatistics", true);
Type typeFieldNameLookup = typeSqlDataReader.Assembly.GetType("System.Data.ProviderBase.FieldNameLookup", true);
BindingFlags staticflags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Static;
BindingFlags instflags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
DynamicMethod dynmethod = new DynamicMethod("SqlDataReader_IndexOf", typeof(int), new Type[2]{ typeSqlDataReader, typeof(string) }, true);
ILGenerator gen = dynmethod.GetILGenerator();
gen.DeclareLocal(typeSqlStatistics);
gen.DeclareLocal(typeof(int));
// SqlStatistics statistics = (SqlStatistics) null;
gen.Emit(OpCodes.Ldnull);
gen.Emit(OpCodes.Stloc_0);
// try {
gen.BeginExceptionBlock();
// statistics = SqlStatistics.StartTimer(this.Statistics);
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Call, typeSqlDataReader.GetProperty("Statistics", instflags | BindingFlags.GetProperty, null, typeSqlStatistics, Type.EmptyTypes, null).GetMethod);
gen.Emit(OpCodes.Call, typeSqlStatistics.GetMethod("StartTimer", staticflags | BindingFlags.InvokeMethod, null, new Type[] { typeSqlStatistics }, null));
gen.Emit(OpCodes.Stloc_0); //statistics
// if(this._fieldNameLookup == null) {
Label branchTarget = gen.DefineLabel();
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.GetField));
gen.Emit(OpCodes.Brtrue_S, branchTarget);
// this.CheckMetaDataIsReady();
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Call, typeSqlDataReader.GetMethod("CheckMetaDataIsReady", instflags | BindingFlags.InvokeMethod, null, Type.EmptyTypes, null));
// this._fieldNameLookup = new FieldNameLookup((IDataRecord)this, this._defaultLCID);
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_defaultLCID", instflags | BindingFlags.GetField));
gen.Emit(OpCodes.Newobj, typeFieldNameLookup.GetConstructor(instflags, null, new Type[] { typeof(IDataReader), typeof(int) }, null));
gen.Emit(OpCodes.Stfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.SetField));
// }
gen.MarkLabel(branchTarget);
gen.Emit(OpCodes.Ldarg_0); //this
gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.GetField));
gen.Emit(OpCodes.Ldarg_1); //name
gen.Emit(OpCodes.Call, typeFieldNameLookup.GetMethod("IndexOf", instflags | BindingFlags.InvokeMethod, null, new Type[] { typeof(string) }, null));
gen.Emit(OpCodes.Stloc_1); //int output
Label leaveProtectedRegion = gen.DefineLabel();
gen.Emit(OpCodes.Leave_S, leaveProtectedRegion);
// } finally {
gen.BeginFaultBlock();
// SqlStatistics.StopTimer(statistics);
gen.Emit(OpCodes.Ldloc_0); //statistics
gen.Emit(OpCodes.Call, typeSqlStatistics.GetMethod("StopTimer", staticflags | BindingFlags.InvokeMethod, null, new Type[] { typeSqlStatistics }, null));
// }
gen.EndExceptionBlock();
gen.MarkLabel(leaveProtectedRegion);
gen.Emit(OpCodes.Ldloc_1);
gen.Emit(OpCodes.Ret);
IndexOf = (IndexOfDelegate)dynmethod.CreateDelegate(typeof(IndexOfDelegate));
}
}
Я написал для пользователей Visual Basic:
Protected Function HasColumnAndValue(ByRef reader As IDataReader, ByVal columnName As String) As Boolean
For i As Integer = 0 To reader.FieldCount - 1
If reader.GetName(i).Equals(columnName) Then
Return Not IsDBNull(reader(columnName))
End If
Next
Return False
End Function
Я думаю, что это более мощно, а использование:
If HasColumnAndValue(reader, "ID_USER") Then
Me.UserID = reader.GetDecimal(reader.GetOrdinal("ID_USER")).ToString()
End If
В одной строке используйте это после извлечения данных DataReader:
var fieldNames = Enumerable.Range(0, dr.FieldCount).Select(i => dr.GetName(i)).ToArray();
Затем
if (fieldNames.Contains("myField"))
{
var myFieldValue = dr["myField"];
...
Edit
Гораздо более эффективный однострочный что не требует загрузки схемы:
var exists = Enumerable.Range(0, dr.FieldCount).Any(i => string.Equals(dr.GetName(i), fieldName, StringComparison.OrdinalIgnoreCase));
Если вы прочитаете вопрос, Майкл спросил о DataReader, а не о DataRecord.
Использование r.GetSchemaTable().Columns.Contains(field)
в DataRecord действительно работает, но оно возвращает столбцы BS (см. Снимок экрана ниже).
Чтобы узнать, существует ли столбец данных и содержит данных в DataReader, используйте следующие расширения:
public static class DataReaderExtensions
{
/// <summary>
/// Checks if a column's value is DBNull
/// </summary>
/// <param name="dataReader">The data reader</param>
/// <param name="columnName">The column name</param>
/// <returns>A bool indicating if the column's value is DBNull</returns>
public static bool IsDBNull(this IDataReader dataReader, string columnName)
{
return dataReader[columnName] == DBNull.Value;
}
/// <summary>
/// Checks if a column exists in a data reader
/// </summary>
/// <param name="dataReader">The data reader</param>
/// <param name="columnName">The column name</param>
/// <returns>A bool indicating the column exists</returns>
public static bool ContainsColumn(this IDataReader dataReader, string columnName)
{
/// See: http://stackoverflow.com/questions/373230/check-for-column-name-in-a-sqldatareader-object/7248381#7248381
try
{
return dataReader.GetOrdinal(columnName) >= 0;
}
catch (IndexOutOfRangeException)
{
return false;
}
}
}
Использование:
public static bool CanCreate(SqlDataReader dataReader)
{
return dataReader.ContainsColumn("RoleTemplateId")
&& !dataReader.IsDBNull("RoleTemplateId");
}
Вызов r.GetSchemaTable().Columns
в DataReader возвращает столбцы BS:
[/g0]
public static bool DataViewColumnExists(DataView dv, string columnName)
{
return DataTableColumnExists(dv.Table, columnName);
}
public static bool DataTableColumnExists(DataTable dt, string columnName)
{
string DebugTrace = "Utils::DataTableColumnExists(" + dt.ToString() + ")";
try
{
return dt.Columns.Contains(columnName);
}
catch (Exception ex)
{
throw new MyExceptionHandler(ex, DebugTrace);
}
}
Columns.Contains
нечувствителен к регистру.
Чтобы ваш код был прочным и чистым, используйте одну функцию расширения, например:
Public Module Extensions
<Extension()>
Public Function HasColumn(r As SqlDataReader, columnName As String) As Boolean
Return If(String.IsNullOrEmpty(columnName) OrElse r.FieldCount = 0, False, Enumerable.Range(0, r.FieldCount).Select(Function(i) r.GetName(i)).Contains(columnName, StringComparer.OrdinalIgnoreCase))
End Function
End Module
Эти ответы уже размещены здесь. Просто немного:
bool b = reader.GetSchemaTable().Rows
.Cast<DataRow>()
.Select(x => (string)x["ColumnName"])
.Contains(colName, StringComparer.OrdinalIgnoreCase);
//or
bool b = Enumerable.Range(0, reader.FieldCount)
.Select(reader.GetName)
.Contains(colName, StringComparer.OrdinalIgnoreCase);
Второй - чище и намного быстрее. Даже если вы не запускаете GetSchemaTable
каждый раз в первом подходе, поиск будет очень медленным.
return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList();
:)
– nawfal
12 December 2013 в 16:03
В вашей конкретной ситуации (все процедуры имеют одни и те же столбцы, кроме 1, который имеет дополнительный 1 столбец), будет лучше и быстрее проверять читателя. Свойство FieldCount, чтобы различать их.
const int NormalColCount=.....
if(reader.FieldCount > NormalColCount)
{
// Do something special
}
Я знаю, что это старый пост, но я решил ответить, чтобы помочь другим в той же ситуации. вы также можете (по соображениям производительности) смешать это решение с решением для итерации решения.
Следующее прост и работает для меня:
bool hasMyColumn = (reader.GetSchemaTable().Select("ColumnName = 'MyColumnName'").Count() == 1);
Как насчет
if (dr.GetSchemaTable().Columns.Contains("accounttype"))
do something
else
do something
Вероятно, это было бы не так эффективно в цикле
Здесь решение из жасмина в одной строке ... (еще один, простой!):
reader.GetSchemaTable().Select("ColumnName='MyCol'").Length > 0;
Вот рабочий пример идеи Ясмина:
var cols = r.GetSchemaTable().Rows.Cast<DataRow>().Select
(row => row["ColumnName"] as string).ToList();
if (cols.Contains("the column name"))
{
}
Мой класс доступа к данным должен быть обратно совместимым, поэтому я могу попытаться получить доступ к столбцу в выпуске, где он еще не существует в базе данных. У нас есть несколько довольно больших наборов данных, которые возвращаются, поэтому я не являюсь большим поклонником метода расширения, который должен перебирать коллекцию столбцов DataReader для каждого свойства.
У меня есть класс утилиты, который создает частный список столбцов, а затем имеет общий метод, который пытается разрешить значение на основе имени столбца и типа выходного параметра.
private List<string> _lstString;
public void GetValueByParameter<T>(IDataReader dr, string parameterName, out T returnValue)
{
returnValue = default(T);
if (!_lstString.Contains(parameterName))
{
Logger.Instance.LogVerbose(this, "missing parameter: " + parameterName);
return;
}
try
{
if (dr[parameterName] != null && [parameterName] != DBNull.Value)
returnValue = (T)dr[parameterName];
}
catch (Exception ex)
{
Logger.Instance.LogException(this, ex);
}
}
/// <summary>
/// Reset the global list of columns to reflect the fields in the IDataReader
/// </summary>
/// <param name="dr">The IDataReader being acted upon</param>
/// <param name="NextResult">Advances IDataReader to next result</param>
public void ResetSchemaTable(IDataReader dr, bool nextResult)
{
if (nextResult)
dr.NextResult();
_lstString = new List<string>();
using (DataTable dataTableSchema = dr.GetSchemaTable())
{
if (dataTableSchema != null)
{
foreach (DataRow row in dataTableSchema.Rows)
{
_lstString.Add(row[dataTableSchema.Columns["ColumnName"]].ToString());
}
}
}
}
Тогда я могу просто вызвать мой код так:
using (var dr = ExecuteReader(databaseCommand))
{
int? outInt;
string outString;
Utility.ResetSchemaTable(dr, false);
while (dr.Read())
{
Utility.GetValueByParameter(dr, "SomeColumn", out outInt);
if (outInt.HasValue) myIntField = outInt.Value;
}
Utility.ResetSchemaTable(dr, true);
while (dr.Read())
{
Utility.GetValueByParameter(dr, "AnotherColumn", out outString);
if (!string.IsNullOrEmpty(outString)) myIntField = outString;
}
}
это работает для меня:
bool hasColumnName = reader.GetSchemaTable().AsEnumerable().Any(c => c["ColumnName"] == "YOUR_COLUMN_NAME");
Hashtable ht = new Hashtable();
Hashtable CreateColumnHash(SqlDataReader dr)
{
ht = new Hashtable();
for (int i = 0; i < dr.FieldCount; i++)
{
ht.Add(dr.GetName(i), dr.GetName(i));
}
return ht;
}
bool ValidateColumn(string ColumnName)
{
return ht.Contains(ColumnName);
}