C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Фильтрация по дате
O.Lena
Отправлено: 13.05.2004, 08:53


Ученик-кочегар

Группа: Участник
Сообщений: 21



Бывалые и опытные, помогите!!!
Для Вас это наверно пара пустяков, а сижу ломаю голову.
Проблема заключается в следующем:
Есть база SQL, таблица с данными о платежах различных организаций и людей.
Необходимо сделать фильтрацию либо за день, либо за период.
В таблице есть поля №платежа, Организация, Дата платежа, Сумма, Дата ввода, Тип платежа (текущий, долг и т.п.) тип плательщика (юр.лицо, физ.лицо).
Соединение с базой идет через файл линка, исп. ADO, на форму накидала три DateTimePicker, один для того чтобы можно было делать отбор за один конкретный день, два других, чтобы можно было задать период для отбора. Подскажите, плиз sad.gif
Gedeon
Отправлено: 13.05.2004, 09:39


Ветеран

Группа: Модератор
Сообщений: 1742



Способов несколько:
1) получать необходимые данные сформировав запрос с указанием нужного периода;
2) фильтровать непосредственно в компоненте.
Три TDateTimePicker на мой взгляд избыточно, 2 в самый раз, не буду вам советовать использовать 1 компонент TrPeriodComboEdit из polaris library (самое красивое решение), поскольку боюсь даже не сможете эту библиотеку установить(но если хотите, поможем, тогда вообще красота будет). Если 2 еще ставте чекбокс с названием за период — отмечен второй TDateTimePicker->Enabled = true; else false;
Если для выборки используется TADOQuery, укажите в нем такой запрос
SQL
SELECT * FROM таблица WHERE [Дата платежа] BETWEEN :First AND :Last

Дальше в вашей программе при каком либо событии, например нажимаем кнопку
CODE

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   ADOQuery1->Parameters->ParamByName("First")->Value = DateTimePicker1->DateTime;
   if(CheckBox1->Cheked){
       ADOQuery1->Parameters->ParamByName("Last")->Value = DateTimePicker2->DateTime;
   }
   else{
       ADOQuery1->Parameters->ParamByName("Last")->Value = DateTimePicker1->DateTime;
   }
   ADOQuery1->Requery();

}
//---------------------------------------------------------------------------

Собственно все.
O.Lena
Отправлено: 13.05.2004, 12:20


Ученик-кочегар

Группа: Участник
Сообщений: 21



Все оказалось немного не так. В той таблице платежей в поле Дата платежа тип данных char. Изменить тип данных на data не могу так как база создавалась не мной и там уже до кучи введено всего. Но тем не менее начальство требует собирать сводку за период и за дату. Подскажите, что делать. Погибаю!!!!! sad.gif
full_lamer
Отправлено: 13.05.2004, 12:25


Машинист паровоза

Группа: Участник
Сообщений: 225



Попробуйте сделать так:
создать новую таблицу в той БД и перегнать туда данные, но менять типа "Дата платежа" при перегоне с char на date... Программка для этого перегона небольшая получится... И уже из новой таблицы сделайте выборку...
Gedeon
Отправлено: 13.05.2004, 13:25


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (O.Lena @ 13/05/2004, 13:22)
Изменить тип данных на data не могу так как база создавалась не мной и там уже до кучи введено всего.

Вы не имеете прав доступа к таблице или просто не знаете как изменить тип данных? Начальству скажите пусть руки поотрывает тому, кто делал эту таблицу.
O.Lena
Отправлено: 14.05.2004, 03:58


Ученик-кочегар

Группа: Участник
Сообщений: 21



В эту базу данные попадают не прямым вводом, а производится прием текстовых файлов, и видимо поэтому для поля дата платежа был определен тип данных char. Может быть как-то возможно перевести тип data в тип char в программе?
full_lamer
Отправлено: 14.05.2004, 05:51


Машинист паровоза

Группа: Участник
Сообщений: 225



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

Можно сделать так:
вот запрос:
select * form [tablename]

Далее в программе:
StrToDate (Query->FieldByName ("[fieldname]")->AsString)

Отредактировано full_lamer — 14/05/2004, 06:57
O.Lena
Отправлено: 14.05.2004, 08:12


Ученик-кочегар

Группа: Участник
Сообщений: 21



Не получается!!!!
full_lamer
Отправлено: 14.05.2004, 08:35


Машинист паровоза

Группа: Участник
Сообщений: 225



Что не получается? Запрос к БД? Или изменения формата?
Gedeon
Отправлено: 14.05.2004, 08:41


Ветеран

Группа: Модератор
Сообщений: 1742



Ну что я могу вам сказть — во первых не надо все усложнять, во вторых раз уж работаете с серверами БД, то учите SQL ибо переложить на сервер с клиента нужно все, что можно, запрос на такой замените:
SQL
SELECT *
FROM таблица WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last

В коде билдэра ничего менять не надо.
QUOTE

В эту базу данные попадают не прямым вводом, а производится прием текстовых файлов, и видимо поэтому для поля дата платежа был определен тип данных char.

Какая разница как попадают данные в базу? О преобразовании типов должен заботиться тот, кто их заносит. Настоятельно рекомендую переделать все по человечески. С таким хранением даты вы теряете быстродействие, больше размер базы(вместо 4 байт SMALDATETIME или 8 — DATETIME Вы получаете минимум 10 байт на строку для каждой записи, плюс потеря времени на преобразование типов.)
Руки поотрывать не забудьте тому разработчику. biggrin.gif
Admin
Отправлено: 14.05.2004, 10:18


Владимир

Группа: Администратор
Сообщений: 1190



А что за база данных ??? (Paradox, Access, FireBird ?)

Базу переделывать не нужно, просто создайте в таблице
новое поле (типа Date или Timestamp — какой тип дат поддерживает
Ваша база) и в него перелейте все данные из char-поля дат.

Тогда и базу переделывать полностью не придется и вcе будет
работать. (старое char-поле с датами можете и не удалять,
просто не работайте с ним и все).

Придется переписать программу ввода данных в базу
(чтоб при переливании преобразовывала в тип Date и
вливала данные уже в это новое поле).

Удобно иcпользовать 2 DateTimePicker-a. (а не 3)
Кстати, если база большая, и поиск/отбор идет не быстро,
я бы посоветовал для отбора за 1 дату (не за период),
делать проверку:
if( DateTimePicker1->Date == DateTimePicker2->Date )
и если указан 1 день, то и запрос задавать для 1 дня:
SELECT * FROM таблица WHERE [Дата платежа] = :First
Выйдет быстрее.
Gedeon
Отправлено: 14.05.2004, 10:35


Ветеран

Группа: Модератор
Сообщений: 1742



Из
QUOTE
Есть база SQL

Я так понял речь идет о SQLServer
В этом случае в дизайнере таблиц достаточно поменять тип данных в поле, которые автоматически преобразуются в DATETIME(SMALDATETIME) Переписывать программу переноса данных в базу в принципе тоже не должно потребовать много усилий, мне видится че-то типа ->AsString поменять на ->AsDateTime и StrToDateTime(правую сторону выражения), хотя судя по типу данных быть там может все, что угодно biggrin.gif , поэтому мож есть смысл даже все с нуля переделать.
O.Lena
Отправлено: 17.05.2004, 05:18


Ученик-кочегар

Группа: Участник
Сообщений: 21



Gedeon, большущее спасибо, наконец-то фильтрация по дате получилась!!! А поотрывать руки тому разработчику не могу, так как он слишком далеко от меня, недосягаем.
O.Lena
Отправлено: 17.05.2004, 11:45


Ученик-кочегар

Группа: Участник
Сообщений: 21



Помогите, пожалуйста. Мда... Плохо я училась в институте. Теперь то все и сказывается. И книжек толковых под рукой нет.

Вот текст запроса:

SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи
WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last
AND [kod_raion] = :Raion
AND [Тип платежа] IN ('01', '02')

в DBGridе есть одно поле, в котором сумма по указанным типам платежей. А как сделать, чтобы в этом DBGride вывести еще два поля с с суммами типов платежей 03, 04 и 05, 06?
База на SQL Server.
olegenty
Отправлено: 17.05.2004, 11:58


Ветеран

Группа: Модератор
Сообщений: 2412



например, можешь ещё несколько таких запросов написать, и по полю Raion Lokkup полем зацепить на один датасет, либо сделать перекрёстный запрос средствами WHEN CASE MSSQL сервера. это возможно, беда лишь в том, что ссылку не могу отковырять...
** pasha
Отправлено: 17.05.2004, 15:28


Не зарегистрирован







Самое простое и неэффективное
(если вообще будет работать)

SQL
SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('01', '02')
union
SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('03', '04')
union
SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('05', '06')
olegenty
Отправлено: 17.05.2004, 15:43


Ветеран

Группа: Модератор
Сообщений: 2412



нет, это работать не будет, получится ПРОСТО сумма
Gedeon
Отправлено: 17.05.2004, 16:01


Ветеран

Группа: Модератор
Сообщений: 1742



Работать то оно будет, только мы получим последовательное обьединение записей по очереди для всех платежей, но задача несколько иная, сегодня чего-то хреновастенько мне, завтра попробую написать запрос.
** pasha
Отправлено: 17.05.2004, 16:25


Не зарегистрирован







Попробавал — работает нормально !

Получим в итоге поле COLUMN1 с 3 суммами (записями):

1 для ('01', '02')
2 для ('03', '04')
3 для ('05', '06')

(можно и другие условия в каждом случае разными задать,
например :First , :Last и :Raion )


AVC
Отправлено: 17.05.2004, 17:04


Не зарегистрирован







Так работать должно (небольшое изменение **pasha)

CODE

Select
(SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('01', '02')) as Sum12
--
,(SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('03', '04')) as Sum34
--
,(SELECT SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [kod_raion] = :Raion AND [Тип платежа] IN ('05', '06')) as Sum56
--
From DUAL /*для Oracle*/
From DUMMY /*для Sybes*/
или что то подобное

O.Lena
Отправлено: 18.05.2004, 11:14


Ученик-кочегар

Группа: Участник
Сообщений: 21



Убрала параметр raion, теперь запрос выглядит так:

SELECT [kod_raion], SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [Тип платежа] IN ('01', '02')
GROUP BY [kod_raion]

В Gridе первое поле выдало все районы, а второе поле, в котором хотелось бы видеть суммы по типу платежа 01, 02 пустое. Также не получаются следующие два поля с типами платежей 03, 04 и 05, 06.
Совет, который дал pasha выдает одно поле с тремя строками, а надо чтобы выглядело так:

Район Платежи 01, 02 Платежи 03, 04 Платежи 05, 06
1-й район Сумма Сумма Сумма
2-й район Сумма Сумма Сумма
и т.д.
AVC
Отправлено: 18.05.2004, 12:14


Не зарегистрирован







Вот еще один вариант:
CODE

Select t0.[kod_raion], t1.[COLUMN1] Sum12, t2.[COLUMN1] Sum34 ...
From (
(Select Distinct [kod_raion] From Платежи WHERE CAST
([Дата платежа] AS DATETIME) BETWEEN :First AND :Last
)
,
(SELECT [kod_raion], SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST
([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [Тип платежа] IN
('01', '02')
GROUP BY [kod_raion]
) t1
,
(SELECT [kod_raion], SUM([Сумма]) AS COLUMN1 FROM Платежи WHERE CAST
([Дата платежа] AS DATETIME) BETWEEN :First AND :Last AND [Тип платежа] IN
('03', '04')
GROUP BY [kod_raion]
) t2
...
Where t1.[kod_raion] (+) = t0.[kod_raion]
  and t2.[kod_raion] (+) = t0.[kod_raion]

(+) == left outer join
синтаксис Oracle для стандарта SQL
в Oracle это можно решить и иначе, но это уже возможности Oracle
Gedeon
Отправлено: 18.05.2004, 14:02


Ветеран

Группа: Модератор
Сообщений: 1742



В MSSQLServer *= и =* сответственно left outer join и right outer join
O.Lena
Отправлено: 19.05.2004, 07:55


Ученик-кочегар

Группа: Участник
Сообщений: 21



Сделала как посоветовал AVC

Where t1.[kod_raion] *= t0.[kod_raion]
and t2.[kod_raion] *= t0.[kod_raion]


Выдается ошибка "Parameter object is improperly defined. Inconsistent or incomplete information was provided." [COLOR=blue]
AVC
Отправлено: 19.05.2004, 08:13


Не зарегистрирован







К сожалению я не знаком с тонкостями синтаксиса MSSql, поэтому к предложенному варианту нужно относиться как к марксизму. Например, в Sybase конструкция (Select a1 ...) t0 дложна выглядеть так (Select a1 ...) as t0 (a1).
O.Lena
Отправлено: 20.05.2004, 10:12


Ученик-кочегар

Группа: Участник
Сообщений: 21



По поводу сообщения об ошибке, надо было мне просто установить значения DataType новых параметров. Это все заработало. Спасибо!!! smile.gif
AVC
Отправлено: 20.05.2004, 10:35


Ветеран

Группа: Модератор
Сообщений: 1583



На будующее — еще один приемчик для того же результата (схема запроса):
CODE

Select -- обрамляющий итоговый запрос
 t0.regionID
,Sum(t0.Sum1) as Sum1
,Sum(t0.Sum2) as Sum2
From
(Select -- показатели по первой группе
   regionID -- regionID вегда первая колонка
  ,Sum(Сумма) as Sum1 -- итоги по первой группе — во вторую колонку
  ,0 as Sum2 -- итоги по второй группе — в третью колонку
From ...
Where ...
Group by regionID
Union all
Select -- показатели по второй группе
   regionID
  ,0 as Sum1
  ,Sum(Сумма) as Sum2
From ...
Where ...
Group by regionID
) t0
Group by t0.regionID

Обратите внимание, что во внутреннем сумировании суммы разных групп попадают в разные колонки разных строк. Обрамляющий select нужен для "схлопывания" всех строк к строкам, у которых заполнены все колонки.
Предложенные способы приводятся во многих учебниках по SQL.
O.Lena
Отправлено: 20.05.2004, 11:15


Ученик-кочегар

Группа: Участник
Сообщений: 21



А вот как сделать, чтобы допустим в четвертой колонке было следующее: итоги по первой группе — вторая колонка минус итоги по второй группе — третья колонка?
AVC
Отправлено: 20.05.2004, 11:23


Ветеран

Группа: Модератор
Сообщений: 1583



CODE

Select -- обрамляющий итоговый запрос
t0.regionID
,Sum(t0.Sum1) as Sum1
,Sum(t0.Sum2) as Sum2

-- Sybase позволяет так
,(Sum1 — Sum2) as Sum3

-- Oracle так
,(Sum(t0.Sum1) — Sum(t0.Sum2)) as Sum3

или так
CODE

Seect -- еще один обрамляющий запрос
 t1.rerioID, t1.Sum1, t1.Sum2, (t1.Sum1-t1.Sum2) as Sum3
From
( все из прошлого примера
) t1

Вернуться в Вопросы программирования в C++Builder