Форматирование результатов запроса для отображения

Задача
Вы хотите вывести результирующее множество красиво отформатированным.

Решение
Прибегните к помощи метаданных. Они могут предоставить вам важную информацию о структуре и содержимом результата.

Обсуждение
Метаданные весьма полезны для форматирования результатов запроса, так как сообщают нам множество полезных вещей, например, имена и ширину вывода столбцов, даже в тех случаях, когда неизвестно, что именно делал запрос. Например, можно написать универсальную функцию, выводящую результирующее множество, в табличной форме, не имея сведений о природе запроса. Рассмотрим фрагмент кода на Java, который получает объект результирующего множества и применяет его для получения метаданных результата. Затем оба объекта используются вместе для извлечения и форматирования значений результирующего множества. Вывод похож на вывод mysql: за строкой с заголовками столбцов следуют строки результата, столбцы которого заключены в красивые рамочки и выровнены по вертикали.

Вот как функция выведет результирующее множество, сформированное запросом SELECT id, name, birth FROM profile:

+---+---------+-------------+
|id |name |birth |
+---+---------+-------------+
|1 |Fred |1970-04-13|
|2 |Mort |1969-09-30|
|3 |Brit |1957-12-01|
|4 |Carl |1973-11-02|
|5 |Sean |1963-07-04|
|6 |Alan |1965-02-14|
|7 |Mara |1968-09-17|
|8 |Shepard |1975-09-02|
|9 |Dick |1952-08-20|
|10 |Tony |1960-05-01|
|11 |Juan |NULL |
+--+----------+-------------+

11 rows selected

Первый вопрос, на который должно ответить такое приложение: «Какова правильная ширина вывода каждого из столбцов?».


Метод getColumnDisplay-Size() возвращает ширину столбца, но необходимо учитывать еще ряд соображений:

• Следует принять во внимание длину имени столбца, которое может превышать длину значений столбца.

• Для значений NULL будет отображаться слово «NULL», поэтому для столбцов, допускающих такие значения, ширина вывода должна быть не меньше четырех символов.
Напишем Java-функцию displayResultSet(), которая форматирует результирующее множество, принимая в расчет перечисленные выше факторы. Кроме того, при извлечении строк она подсчитывает их количество, так как в JDBC получить это значение непосредственно из метаданных невозможно.

public static void displayResultSet (ResultSet rs) throws SQLException
{
ResultSetMetaData md = rs.getMetaData ();
int ncols = md.getColumnCount ();
int nrows = 0;
int[] width = new int[ncols + 1]; // массив для хранения ширины столбцов
StringBuffer b = new StringBuffer (); // буфер для хранения ограничивающей линии
// вычисление ширины столбцов
for (int i = 1; i <= ncols; i++)
{
// некоторые драйверы возвращают -1 для getColumnDisplaySize();
// если это так, то замещаем его длиной имени столбца
width[i] = md.getColumnDisplaySize (i);
if (width[i] < md.getColumnName (i).length ())
width[i] = md.getColumnName (i).length ();
// isNullable() возвращает 1/0, а не истина/ложь
if (width[i] < 4 && md.isNullable (i) != 0)
width[i] = 4;
}
// формируем строку +---+---...
b.append ("+");
for (int i = 1; i <= ncols; i++)
{
for (int j = 0; j < width[i]; j++)
b.append ("-");
b.append ("+");
}
// выводим ограничительную линию, заголовки столбцов, еще линию
System.out.println (b.toString ());
System.out.print ("|");for (int i = 1; i <= ncols; i++)
{
System.out.print (md.getColumnName (i));
for (int j = md.getColumnName (i).length (); j < width[i]; j++)
System.out.print (" ");
System.out.print ("|");
}
System.out.println ();
System.out.println (b.toString ());
// выводим содержимое результата
while (rs.next ())
{
++nrows;
System.out.print ("|");
for (int i = 1; i <= ncols; i++)
{
String s = rs.getString (i);
if (rs.wasNull ())
s = "NULL";
System.out.print (s);
for (int j = s.length (); j < width[i]; j++)
System.out.print (" ");
System.out.print ("|");
}
System.out.println ();
}
// выводим ограничительную линию и количество строк
System.out.println (b.toString ());
if (nrows == 1)
System.out.println ("1 row selected");
else
System.out.println (nrows + " rows selected");
}

Если вы хотите придать выводу более законченный вид, то можете провести проверку столбца на числовые значения и, если столбец числовой, выровнять его по правому краю.


В сценариях DBI и PHP это легко сделать, так как можно обратиться к атрибутам метаданных mysql_is_num или numeric, поддерживаемым этими API. В DB-API и JDBC все несколько сложнее. Значения метаданных, показывающего, является ли столбец числовым, не существует, так что приходится смотреть на индикатор типа столбца, чтобы понять, относится ли он к одному из числовых типов.

Недостатком функции displayResultSet() также является то, что она выводит столбцы, используя ширину, указанную в определении таблицы, а не фактическую ширину значений, реально присутствующих в результирующем множестве (которая часто бывает меньше первой). Посмотрим на пример вывода, приведенный перед функцией displayResultSet(). Столбцы id и name имеют ширину 10 и 20 символов, несмотря на то, что их самые большие значения имеют длину, равную двум и семи символам соответственно. В DBI, PHPи DB-API вы можете получить максимальную ширину значений результирующего множества. Для того чтобы определить эти значения в JDBC, следует обойти результирующее множество и самостоятельно проверить длины значений столбцов. Необходим драйвер JDBC 2.0, поддерживающий прокрутку результирующих множеств. Если предположить, что вы работаете именно с этим драйвером, то можно изменить код displayResultSet() так:

// вычислить ширину столбцов
for (int i = 1; i <= ncols; i++)
{
width[i] = md.getColumnName (i).length ();
// isNullable() возвращает 1/0, а не истина/ложь
if (width[i] < 4 && md.isNullable (i) != 0)
width[i] = 4;
}
// прокрутить результирующее множество и скорректировать ширину вывода
while (rs.next ())
{
for (int i = 1; i <= ncols; i++)
{
byte[] bytes = rs.getBytes (i);
if (!rs.wasNull ())
{
int len = bytes.length;
if (width[i] < len)
width[i] = len;
}
}
}
rs.beforeFirst (); // "перемотать" результат обратно перед выводом

После проделанных изменений результирующее множество выглядит более компактно:

+--+---------+-------------+
|id |name |birth |
+--+---------+-------------+
|1 |Fred |1970-04-13|
|2 |Mort |1969-09-30|
|3 |Brit |1957-12-01|
|4 |Carl |1973-11-02|
|5 |Sean |1963-07-04|
|6 |Alan |1965-02-14|
|7 |Mara |1968-09-17|
|8 |Shepard|1975-09-02 |
|9 |Dick |1952-08-20 |
|10|Tony |1960-05-01 |
|11|Juan |NULL |
+--+---------+-------------+

11 rows selected.



Оцените статью: (0 голосов)
0 5 0

Статьи из раздела MySQL на эту тему:
Вывод списков таблиц и баз данных
Мониторинг сервера MySQL
Определение количества строк, обработанных запросом
Определение наличия или отсутствия результирующего множества
Определение текущего пользователя MySQL