O.Lena |
Отправлено: 13.05.2004, 08:53 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 21
|
Бывалые и опытные, помогите!!!
Для Вас это наверно пара пустяков, а сижу ломаю голову.
Проблема заключается в следующем:
Есть база SQL, таблица с данными о платежах различных организаций и людей.
Необходимо сделать фильтрацию либо за день, либо за период.
В таблице есть поля №платежа, Организация, Дата платежа, Сумма, Дата ввода, Тип платежа (текущий, долг и т.п.) тип плательщика (юр.лицо, физ.лицо).
Соединение с базой идет через файл линка, исп. ADO, на форму накидала три DateTimePicker, один для того чтобы можно было делать отбор за один конкретный день, два других, чтобы можно было задать период для отбора. Подскажите, плиз |
|
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 не могу так как база создавалась не мной и там уже до кучи введено всего. Но тем не менее начальство требует собирать сводку за период и за дату. Подскажите, что делать. Погибаю!!!!! |
|
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
|
Есть такой — чисто ламерский вариант: последовательным запросом — только по одной записи выхватывать из базы и потом узе в проге преобразовывать тип и отбирать... ну к примеру создать левую таблицу на Парадоксе и туда все сгружать...
Можно сделать так:
вот запрос:
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 байт на строку для каждой записи, плюс потеря времени на преобразование типов.)
Руки поотрывать не забудьте тому разработчику.
|
|
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
|
Из
Я так понял речь идет о SQLServer
В этом случае в дизайнере таблиц достаточно поменять тип данных в поле, которые автоматически преобразуются в DATETIME(SMALDATETIME) Переписывать программу переноса данных в базу в принципе тоже не должно потребовать много усилий, мне видится че-то типа ->AsString поменять на ->AsDateTime и StrToDateTime(правую сторону выражения), хотя судя по типу данных быть там может все, что угодно , поэтому мож есть смысл даже все с нуля переделать.
|
|
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 новых параметров. Это все заработало. Спасибо!!! |
|
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
| |
|