Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ms-access. Показать все сообщения
Показаны сообщения с ярлыком ms-access. Показать все сообщения

пятница, 24 января 2020 г.

Группировка по месяцам даты SQL

#sql #delphi #ms_access


Есть некая таблица с датами и прочей инфой. необходимо сгруппировать инфу только
по месяцам. Вот что я попробовал

Select наименование, month(дата)
FROM таблица1
Group by month(дата), наименование


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


Ответы

Ответ 1



Ни в чём не ошибся, если нужен анализ продаж по месяцам, то там и так будет одна из агрегатных функций или COUNT или SUM и приведённый код будет нормально работать. Если работает только, если дата полностью совпадает, значит не правильно выделяете месяц. Тонкостей MS-Access не помню, а в PostgreSQL это будет выглядеть следующим образом: SELECT month, COUNT(*) FROM art --Или SUM(fieldname) JOIN LATERAL EXTRACT(MONTH FROM дата) month ON TRUE --Просто чтобы не писать то же самое в GROUP BY WHERE дата IS NOT NULL GROUP BY month ORDER BY month Ну а если всё-таки Наименование, то естественно записей будет не 12, а будет зависеть от количества различный наименований проданных в данные месяца. Хотя, если из Delphi, то это даже удобнее будет, там в табличке группировку сделаете по месяцам и будет плюсиком красиво разворачиваться каждый месяц с суммой в footer

пятница, 10 января 2020 г.

Подскажите корректную архитектуру для программы, которая работает с бд

#c_sharp #sql #net #office #ms_access


Писал программу, которой на вход подаётся бд access  и выполняются различные преобразования
,и столкнулся с тем, что если поменяется что то во входной структуре бд, то придётся
жестко рефакторить код... Дело в том, что я работаю через интероп, в некоторых местах
делаю запросы на обновления, которые закодированы в коде, в другом месте, где требуется
мощь высокоуровневых языков я открываю recordset и произвожу обновления записей. Можно
грузить все в память и работать через datatable, но я побоялся больших баз и отказался
от этой затеи... Подскажите, лучше всего уйти от того, что есть к коду, который легко
редактировать если структура поменяется, сохраняя высокую скорость обработки. 
    


Ответы

Ответ 1



Создать в БД таблицу с конфигом базы. И сходя из этого формировать SQL-запросы. Например, создаём таблицу info описанием таблиц. CREATE TABLE info ( ID int identity (1, 1) primary key ,NameTable varchar(50) ,NameColumn varchar(50) ) При запуске приложения читаем эту таблицу, и формируем запросы для других таблиц. Например загружаем таблицу в DataTable c именем infoTable: string command = "SELECT [ID] "; foreach (DataRow row in infoTable.Rows) { command = command + ",[" + row[2].ToString() + "]"; } command = command + " FROM [" + row[1].ToString() + "]"; В итоге в command получается запрос типа: SELECT [ID], [NameColumn1], [NameColumn2] FROM [NameTable] И так далее... Меняется таблица в БД, меняем описание, запросы формируются иначе.

Ответ 2



Еще как вариант изменить программу таким образом чтобы она только выполняла внешние скрипты (в виде файликов 001_update_table1.sql), которые вы будите при необходимости редактировать. В таком случае: Вам не будет необходимости все время пересобирать и переписывать реализацию программы. Можно реализовать поддержку различные версии БД. Правки в одном месте (только в скриптах), не нужно выискивать переменные в DataSet'ах иди захардкоженых переменных.

Ответ 3



А может быть на входе подавать не только бд, а и пакет изменений. Структуру которая будет описывать такой список изменений можно уже разработать в зависимости от поставленных задач. Таким образом при изменении бд вам не нужно будет редактировать код, а только файл с необходимыми изменениями.

пятница, 27 декабря 2019 г.

Создание Add-in для Office в Visual Studio

#c_sharp #net #ms_access


Поставил пакет разработки расширений для офиса, но в списке шаблонов не могу найти
Access, хотя все остальные шаблоны офисных проектов присутствуют(Word, Excel и т д)

Подскажите, для Access нельзя разрабатывать расширения или нужно что-то дополнительно
поставить?

Не помешал бы так же, по возможности, банальный пример, например, при открытии Access
вылетает какой-нибудь hello world. 



Нашел какой-то платный инструментарий. Однако, неужели нет ничего, что доступного
из коробки?
    


Ответы

Ответ 1



Официальная позиция Microsoft заключается в том, что создание надстроек (VSTO) для Access НЕ поддерживается. В Visual Studio нет шаблона для создания любого типа надстройки VSTO для Access. Однако, это не значит, что ничего нельзя сделать. Можно попробывать инструменты рекомендованные @AleXander или приглядеться к Office Access Web App

Ответ 2



Есть такие инструменты для Access'a http://www.skrol29.com/us/vtools.php И к ним есть исходные коды http://www.skrol29.com/us/sourcecode.php Может быть это поможет :} Погуглил и нашел еще кое-что: Есть шаблон проекта vsto плагина Access'a для vs2010 gallery.technet.microsoft.com/office/VSTO-Access-Template-4dedb360 Обертка, позволяющая создавать плагины для access в том числе netoffice.codeplex.com Там есть примеры! Нашел еще слегка костыльный вариант создания VSTO плагина для Access'a - создать из шаблона для Word и немного изменить код: blogs.msdn.microsoft.com/andreww/2008/02/19/vsto-add-ins-for-access

воскресенье, 22 декабря 2019 г.

Как быстро склеить зависимые записи в одну строку

#c_sharp #sql #net #ms_access #office_interop


Допустим, есть 2 таблицы:

1)Должность

2)Персонал, которая ссылается на Должность.

На одну запись Должность могут приходится несколько записей из Персонал.

Задача обновить [Весь персонал] в Должность склеенными в одну строчку зависимыми
записями из Персонал.

Допустим приходится на 1 запись приходится 2 строчки ФИО из Персонал, мы их склеиваем
и записываем в родительскую запись Должность.

Как это сделать быстро? Используется БД Access.

Сейчас я использую Interop, открываю RecordSet из Должность, и иду сверху вниз, и
на каждую запись я открываю второй RecordSet из запроса 'Select PID,FIO From [Персонал]
where PID='rst.fields[ID].value и иду сверху вниз и склеиваю значения, а потом возвращаю
и обновляю их в первом RecordSet и мне кажется, что слишком это тормознуто и можно
как то быстрее сделать это.

Наверное, через ADO.NET было бы быстрее, но мне кажется, что если БД Access будет
здоровой, то слишком жирно все в память грузить разом...

UPD:
Если все таки загрузить все в память и работать через ADO.NET, я получу значительный
выигрыш в скорости?
    


Ответы

Ответ 1



Можно написать VB функцию, которая будет склеивать строку. Подробно можно прочитать тут: http://hiprog.com/index.php?option=com_content&task=view&id=334&Itemid=35 Тело функции: Public Function UnionStr1(ID, Fam) Static IDOld, FamUnion If IDOld <> ID Then IDOld = ID FamUnion = Null End If FamUnion = (FamUnion + ", ") & Fam UnionStr1 = FamUnion End Function Пример запроса: Select ID, Last(UnionStr1(ID,Fam)) AS FamUnion FROM Tab1 GROUP BY ID; В той же статье есть интересный способ. Только средствами ms-access sql, с промежуточной таблицей. Хотя возможно в вашем случае её роль сыграет таблица Person(должностей). Выглядит примерно так: Метод 3. Заполнение таблицы при помощи запросов (по Митину). http://c85.cemi.rssi.ru/Access/AnsPointDetail.idc?QID=14147 Оригинальный и неочевидный метод. Выполняется с помощью двух запросов. 1. Запись в Tab2 уникальных ID без фамилий. Текст запроса WriteID: INSERT INTO Tab2 (ID) Select DISTINCT ID FROM Tab1; 2. Запись списков фамилий. Текст запроса TabUnion5: UPDATE Tab2 INNER JOIN Tab1 On Tab2.ID = Tab1.ID Set Tab2.FamUnion = ([Tab2].[FamUnion]+", ") & [Tab1].[Fam]; Оценка скорости: по-видимому, работает быстрее метода 1 за счет отсутствия вызова специальных функций. Недостаток: "Однако в T-SQL это непрокатывает" (Митин). Надо побаловаться, выглядит интересно. UPD: Смысл в том, что из-за "глупости" ms-access SET будет выполнятся столько раз, сколько записей в присоединённой таблице(не той, которая апдейтится). Из-за "умности" T-SQL, в T-SQL этот способ работать не будет. К минусам этого способа я бы ещё добавил потребность в сопровождении при переходе на новую версию access. Если MS решат приблизить хоть чуть-чуть ms-access к стандарту ANSI-SQL, этот метод может перестать работать. С другой стороны, может они и для конкатенации строк что-нибудь сделают:) Хотя в этом вопросе даже MS SQL Server обделён вниманием до сих пор. MySQL, PostgreSQL, Oracle это давно умеют.

Ответ 2



Я бы делал так: Допустим в таблице персонал(Person) есть три поля: Person_ID, Person_FIO, Position_ID в таблице должность(Position) есть три поля: Position_ID, Position_Name, Position_StaffList Для начала пишем запрос для DataReader'a (если я правильно понимаю это C# аналог Recordset из Basic): SELECT pos.Position_ID, pos.Position_Name, prs.Person_FIO FROM Position AS pos LEFT OUTER JOIN Person AS prs ON pos.Position_ID = prs.Position_ID ORDER BY pos.Position_ID, prs.Person_FIO; И служебный класс internal class Pos { public Pos() { StaffList = new StringBuilder(); } public int? Position_ID {get; set;} public string Position_Name {get; set;} public StringBuilder StaffList {get; set;} } Открываем ридер для нашего запроса. List positions = new List(); int previos_pos = -1; if (reader.HasRows) { int id; string position_name; string fio; Pos current; while (reader.Read()) { id = reader.GetInt32(0); position_name = reader.GetString(1); fio = reader.GetString(2); if (id != previos_pos) { current = new Pos(); positions.Add(current); current.Position_ID = id; current.Position_Name = position_name; current.StaffList.Append(fio); } else { current.StaffList.AppendFormat("{0},{1}" , current.StaffList, , fio ); } } } Закрываем наш ридер. Теперь у нас есть заполненный список positions и остается только обновить из него вашу таблицу позиции. Так как мы не ддосим базу промежуточными запросами (что может быть действительно долго если позиций много), то мы должны получить некоторое ускорение.

Ответ 3



Как это сделать быстро? Используется БД Access. Возможно ужасно знаю способности MS Access... но предполагаю, что сделать эту задачу средствами БД Access невозможно, т.к. там слабый потенциал аналитических функций и расширений SQL запросов. В более способных СУБД в которых язык SQL расширяют доп.аналитическими функциями, есть способы выполнить такую задачу, но даже они выглядят ужасно и криво - например в Oracle с помощью компоновки аналитических функций в нескольких подзапросах. Поэтому для вас самый идеальный вариант тот, что вы уже делаете - сформировать запрос должностей, переходом по должностям, выполнять второй запрос для получения всех ФИО из этой должности. Обрабатывать полученные списки. Предложенный вами вариант действий: Допустим приходится на 1 запись приходится 2 строчки ФИО из Персонал, мы их склеиваем и записываем в родительскую запись Дожность это ужасное и грубое нарушение структуры данных и логики (о нормальных формах БД надо почитать). Делается вывод - у вас скорее всего неверная постановка задачи, либо неверное принятие решения для ее выполнения. PS: поясняю.. если вы все ФИО принадлежащие для 1 должности засунете в поле этой должности, то выполняя запрос к этой должности и выводя информацию на клиенте - вы ничем не сэкономите ОЗУ, т.к. действует математическое правило "от перемены мест слагаемых сумма не меняется". Не важно получите вы эти ФИО и будете хранить в разных полях, либо в одном поле, количество байт символов не изменится, во всяком случае разница будет не заметна для ПК. Но зато когда вы будете получать список известных должностей - то объемы передаваемой информации будут расти очень быстро, что увеличит нагрузку на трафик и БД. Лучше сформулируйте задачу поставленную перед вами, чтобы вам смогли подсказать оптимальное решение, т.к. в данном вопросе вы просите помочь вам выполнить решение которое приняли самостоятельно - но оно ошибочное.

воскресенье, 8 декабря 2019 г.

Как выполнить Сжатие и восстановление Access базы через Interop?

#c_sharp #net #com #ms_access #office_interop


Как выполнить Сжатие и восстановление Access базы через Interop?

Есть класс, который содержит метод:

public bool TryCompactAndRepair()
{
    string pathToDb = app.CurrentDb().Name;
    string pathToCompact = Path.Combine(Path.GetDirectoryName(pathToDb), 
        Path.GetFileNameWithoutExtension(pathToDb)) + 
        "_compact" + Path.GetExtension(pathToDb);

    db.Close();
    app.CloseCurrentDatabase();
    //   Marshal.ReleaseComObject(db);
    GC.Collect();
    if (app.CompactRepair(pathToDb, pathToCompact, true))
    {
        File.Delete(pathToDb);
        File.Move(pathToCompact, pathToDb);
        return true;
    }
    else
    {
        File.Delete(pathToCompact);
    }
    return false;
}


переменная app имеет тип Application.

Почему-то такой метод возвращает различные ошибки:


В текущем виде, что-то вроде "Object no longer set"
Если раскоментить Marshal.ReleaseComObject(db); ,то получаю ошибку, что доступ к
объекту не доступен.
Если вообще не делать Close, то вылетает ошибка, что нельзя сделать сжатие для текущей БД.


Подскажите, в чем косяк?
    


Ответы

Ответ 1



Вызов app.CurrentDb().Name создает управляемую обертку COM-объекта dao.Database, ссылка на которую нигде не сохраняется, так что ее никак нельзя впоследствии освободить, кроме как финализатором. Из-за этого программа держит базу даже после вызовов app.CloseCurrentDatabase() и Marshal.ReleaseComObject на втором экземпляре объекта. Вызов GC.Collect() не помогает, так как финализатор выполняется не сразу, а только через некоторое время. Чтобы нормально освободить базу, нужно избавиться от создания второго экземпляра объекта (так как он не нужен): dao.Database db = app.CurrentDb(); string pathToDb = db.Name; string pathToCompact = Path.Combine(Path.GetDirectoryName(pathToDb), Path.GetFileNameWithoutExtension(pathToDb)) + "_compact" + Path.GetExtension(pathToDb); app.CloseCurrentDatabase(); Marshal.ReleaseComObject(db); app.CompactRepair(pathToDb, pathToCompact, true);

среда, 19 июня 2019 г.

Запрос на выборку данных sql

Для каждой должности сотрудников подсчитать число сотрудников и число заказов.
пока я мучаюсь с выводом должности и количеством сотрудников для каждой должности. Вот что выводиться...
Naimenovanie...........Kolichestvo
официант/курьер....5



SELECT Dolzhnosti.Naimenovanie, COUNT(Sotrudniki.Kod_dolzhnosti) AS Kolichestvo FROM Dolzhnosti INNER JOIN (FormirovanieZakaza INNER JOIN Sotrudniki ON FormirovanieZakaza.Kod_sotrudnika = Sotrudniki.Kod_sotrudnika) ON Dolzhnosti.Kod_dolzhnosti = Sotrudniki.Kod_dolzhnosti GROUP BY Dolzhnosti.Naimenovanie;


Ответ

SELECT Dolzhnosty.Kod_dolzhnosty, COALESCE(T.kolichestvo_sotr, 0) as kolichestvo_sotr, COUNT(*) as kolichestvo_zakazov FROM Dolzhnosty --LEFT нужны для того, чтобы не потерять должности, у которых нет сотрудников --или есть сотрудники, которые не формировали заказы. LEFT JOIN ( SELECT Kod_dolzhnosty, COUNT(*) as kolichestvo_sotr FROM Sotrudniki GROUP BY Kod_dolzhnosty ) as T ON Dolzhnosty.Kod_dolzhnosty = T.Kod_dolzhnosty LEFT JOIN Sotrudniki ON Dolzhnosty.Kod_dolzhnosty = Sotrudniki.Kod_dolzhnosty LEFT JOIN FormirovanieZakaza ON FormirovanieZakaza.Kod_sotrudnika = Sotrudniki.Kod_sotrudnika GROUP BY Dolzhnosty.Kod_dolzhnosty

четверг, 15 ноября 2018 г.

Как быстро склеить зависимые записи в одну строку

Допустим, есть 2 таблицы:
1)Должность
2)Персонал, которая ссылается на Должность.
На одну запись Должность могут приходится несколько записей из Персонал.
Задача обновить [Весь персонал] в Должность склеенными в одну строчку зависимыми записями из Персонал.
Допустим приходится на 1 запись приходится 2 строчки ФИО из Персонал, мы их склеиваем и записываем в родительскую запись Должность.
Как это сделать быстро? Используется БД Access.
Сейчас я использую Interop, открываю RecordSet из Должность, и иду сверху вниз, и на каждую запись я открываю второй RecordSet из запроса 'Select PID,FIO From [Персонал] where PID='rst.fields[ID].value и иду сверху вниз и склеиваю значения, а потом возвращаю и обновляю их в первом RecordSet и мне кажется, что слишком это тормознуто и можно как то быстрее сделать это.
Наверное, через ADO.NET было бы быстрее, но мне кажется, что если БД Access будет здоровой, то слишком жирно все в память грузить разом...
UPD: Если все таки загрузить все в память и работать через ADO.NET, я получу значительный выигрыш в скорости?


Ответ

Можно написать VB функцию, которая будет склеивать строку. Подробно можно прочитать тут:
http://hiprog.com/index.php?option=com_content&task=view&id=334&Itemid=35
Тело функции:
Public Function UnionStr1(ID, Fam) Static IDOld, FamUnion If IDOld <> ID Then IDOld = ID FamUnion = Null End If
FamUnion = (FamUnion + ", ") & Fam UnionStr1 = FamUnion End Function
Пример запроса:
Select ID, Last(UnionStr1(ID,Fam)) AS FamUnion FROM Tab1 GROUP BY ID;
В той же статье есть интересный способ. Только средствами ms-access sql, с промежуточной таблицей. Хотя возможно в вашем случае её роль сыграет таблица Person(должностей). Выглядит примерно так:
Метод 3. Заполнение таблицы при помощи запросов (по Митину). http://c85.cemi.rssi.ru/Access/AnsPointDetail.idc?QID=14147
Оригинальный и неочевидный метод. Выполняется с помощью двух запросов. 1. Запись в Tab2 уникальных ID без фамилий. Текст запроса WriteID: INSERT INTO Tab2 (ID) Select DISTINCT ID FROM Tab1;
2. Запись списков фамилий. Текст запроса TabUnion5: UPDATE Tab2 INNER JOIN Tab1 On Tab2.ID = Tab1.ID Set Tab2.FamUnion = ([Tab2].[FamUnion]+", ") & [Tab1].[Fam];
Оценка скорости: по-видимому, работает быстрее метода 1 за счет отсутствия вызова специальных функций. Недостаток: "Однако в T-SQL это непрокатывает" (Митин).
Надо побаловаться, выглядит интересно.
UPD: Смысл в том, что из-за "глупости" ms-access SET будет выполнятся столько раз, сколько записей в присоединённой таблице(не той, которая апдейтится). Из-за "умности" T-SQL, в T-SQL этот способ работать не будет.
К минусам этого способа я бы ещё добавил потребность в сопровождении при переходе на новую версию access. Если MS решат приблизить хоть чуть-чуть ms-access к стандарту ANSI-SQL, этот метод может перестать работать.
С другой стороны, может они и для конкатенации строк что-нибудь сделают:) Хотя в этом вопросе даже MS SQL Server обделён вниманием до сих пор. MySQL, PostgreSQL, Oracle это давно умеют.