Struktura bazy danych - burza mózgów

0

Temat nieco niejasny ale nie miałem innego pomysłu. Jeżeli możecie proszę o ciekawe sugestie dotyczące rozwiązania problemu.

Środowisko:

  • Platforma .NET 2.0
  • Język (C# - z poziomu .net to w zasadzie drugorzędna sprawa)

Zadanie:
Buduję harmonogram do zliczania godzin pracy. Podzielcie się pomysłami/opiniami dotyczącymi sposobu organizacji danych na dysku (SQL, XML, binarka/ CSV).

Rozmiar:
200 osób * 12 miesięcy * 31 dni (liczba dni stała) * 5 rodzajów obecności na dzień.

Struktury:
Jeden dzień dla jednego pracownika to najlepiej struktura: Skrót (string[2]), Ilość (int), komentarz (string[80])

Jak najlepiej zorganizować dostęp do danych?

  • Tradycyjny FileStream i Serializacja (Binary formatter)
  • SQL (nie do końca wiem jak zrealizować w/w bazę)
  • CSV - może być problem, bo to jednak tablica wielowymiarowa array [1..200, 1..12, 1..31, 1..5] of struktura
  • XML (wersja binarna lub tekstowa może?) - nie do końca rozgryzłem zagadnienie

Wymagane:

  • jak najmniejszy rozmiar bazy
  • możliwość dodawania/usuwania pracownika

Zalecane:
*Logowanie (autoryzacja windows)

To by było na tyle.

0

Opcja 1 i 4 (binarka i XML) są podobne. Przy XML będzie trochę więcej kodzenia i większa będzie baza więc raczej odpada. XML przydaje się wtedy, kiedy wymagana jest możliwość ręcznej edycji bazy (jakimś powszechnie dostępnym edytorem - przy XML edytorem tekstu).

Według mnie najlepsza będzie baza danych. Rozmiar nie duży (chyba nawet mniejszy niż przy binarce bo baza się jakoś sensownie organizuje w drzewka itp.), a przede wszystkim łatwość w oprogramowaniu ! Bardzo prosto można wyświetlać zapytania w DataGridView, można podpinać kontrolki bezpośrednio pod rekord (DataBindings) i wiele innych przydatnych smaczków.

0

Jako standardowy leniwiec zgadzam się z przedmówcą.
Jak będziesz sam organizował 'storage' to się później powiesisz, jak trzeba będzie wybrać dane, albo wygenerować raport. Lepiej się potrudzić nad strukturą bazy, a później niech się samo robi ;) (poza tym jednoczesny dostęp z kilku kompów itp. - nie możesz wykluczyć, że nie będzie w przyszłości potrzebny)

0

Szybki test na SQL Server 2005 Express:

CREATE TABLE [dbo].[worktable](
	[id] [int] IDENTITY(1,1) NOT NULL,
	[id_prac] [int] NOT NULL,
	[from] [datetime] NOT NULL,
	[to] [datetime] NOT NULL,
	[type] [int] NOT NULL,
	[comment] [ntext] NULL,
	[short_term] [nchar](2) NULL,
 CONSTRAINT [PK_worktable] PRIMARY KEY CLUSTERED 
(
	[id] ASC
)


declare @year int
declare @month int
declare @day int
declare @prac int

declare @from datetime
declare @to datetime

set @year = 2005
set @month = 1
set @day = 1
set @prac = 1

while @year < 2009
begin
	while @month <= 12
	begin
		while @day <= 31
		begin
			while @prac <= 200
			begin 
				set @from = '1900-1-1 00:00:00'
				set @from = dateadd(yy, @year - 1900, @from)
				set @from = dateadd(m, @month - 1, @from)
				set @from = dateadd(d, @day - 1, @from)
				set @to = @from
				set @from = dateadd(hh, round(rand() * 3 + 7,0), @from)
				set @to = dateadd(hh, round(rand() * 3 + 15,0), @to)
				insert into worktable (id_prac, [from], [to], type, comment, short_term)
					values(@prac, @from, @to, round(rand() * 5 + 1,0), 'adsufh awjfkhdsjadsgf', 'aa') 
				set @prac = @prac + 1
			end
			set @prac = 1
			set @day = @day + 1
		end
		set @day = 1
		set @month = @month + 1
	end
	set @month = 1
	set @year = @year + 1
end

I przyklad raportu, zeby sprawdzic wydajnosc (sredni dzienny czas pracy):

select year([from]), month([from]), day([from]), avg(datediff(hh, [from], [to]))
from worktable
group by year([from]), month([from]), day([from])
order by year([from]), month([from]), day([from])

Baza obejmuje 3 lata dla 200 pracownikow, zawiera 297600 wierszy i zajmuje ok. 48MB ;)

Generowanie tabeli to jakies 20s na moim kompie (hp dv5 1199ew), zaden z niego potezny serwer. Raport ok 0,5s.

Reasumujac: nie ma sie czego bac :D Pytanie tylko czy tabela odzwierciedla to co chciales uzyskac, ale tak to zrozumialem. W razie czego napisz wiecej, to mozna szybko sprawdzic.

0

SQL mnie przeraża bo...
user image

Plik ma takową strukturę. On musi być w takiej formie graficznej. Nie wiem czy nie popełnię samobójstwa podczas tworzenia widoku czy czegoś takiego. O insertach nie wspomnę. Obsługa musi być głupotoodporna. Mistrzowie na produkcji mogą sobie nie poradzić. Plik binarny byłby dla mnie łatwiejszy, tym bardziej, że z tego ma być generowane podsumowanie miesiąca dla pracownika.

0

ADO.NET i DataGridView są bardzo przyjemne więc problemu raczej nie będzie, w ostateczności możesz nawet wyświetlać komórki we własnej kontrolce ;)

0

Rozumiem Twoje rozterki, ale tu masz wybór następujący:

  • albo będziesz miał skomplikowane generowanie widoku, za to manipulacje danymi proste (sql każdy zna i lubi ;))
  • albo będziesz miał struktórę, którą jednocześnie jest taka jak widok, za to manipulacja tą strukturą będzie piekłem.

Tak ja to przynajmniej widzę.

Aha jeszcze jedno - jeśli jakąś strukturę można zapisać w xml-u prawie na pewno można ją zapisać w relacyjnej bazie danych. Tym bardziej jak wspomniałeś masz wielowymiarowe tablice od struct do zapisania.

0

Ja bym sie nie zastanawial nawet tylko z miejsca bral SQL'a. Nie takie struktury sie tam tworzy i hulaja az milo. Widok napiszesz raz, stracisz na nim 3 dni i bedzie dzialal do konca swoich dni. Strukture napiszesz w te same 3 dni, bedziesz testowal 3 tygodnie i klal nastepne 3 lata na zyczenia klienta co do zmian w niej.

PS. Obrazek jest malutki i niewiele mowi, ale to ciagle obraz dwuwymiarowy czyli jak nic pasuje do tabel i wierszy ;) Napisz konkretna strukture to w wolnych chwilach ktos na pewno pomoze z zapytaniem :)

0

Kurcze, SQL odpadnie. Naprawdę nie mam pomysłu na to jak ogarnąć to coś. Struktura sama w sobie jest debilnie prosta bo:

USE [Harmonogram];

CREATE TABLE Employees(
  IDEmployee INTEGER PRIMARY KEY IDENTITY (1,1),
  LastName   VARCHAR (30),
  FirstName  VARCHAR (30),
  PersonalNo INTEGER NOT NULL, 
  Crew       CHAR(1), 
  WorkArea   VARCHAR(3), 
  DefWorkSys SMALLINT -- system pracy: 12h, 8h
);

CREATE TABLE ScheduleCodes(
  IDScheduleCode INTEGER PRIMARY KEY IDENTITY (1,1),
  CodeName       VARCHAR(2)
);

CREATE TABLE WorkDays(
  IDWorkDay    INTEGER PRIMARY KEY IDENTITY (1,1),
  EmployeeID   INTEGER FOREIGN KEY REFERENCES Employees(IDEmployee),
  ScheduleCode INTEGER FOREIGN KEY REFERENCES ScheduleCodes(IDScheduleCode),
  HourCount    SMALLINT
);

Tylko, że jak ja mam to teraz zainicjować tak aby odzwierciedlić strukturę, czyli dodać dni, gdzie pracownik nie pracował. Wkleję jeszcze raz obrazek. Obetnę dni z 31 na 10. Obecnie harmonogram działa na excelu i działałby dalej gdyby nie fakt, że zaszła potrzeba wydawać np urlop na godziny. Do tej pory było jeden dzień jeden skrót (np UW - urlop wypoczynkowy). Teraz trzeba dawać np UW-4h/OP-4h/UB-4h i właśnie to rozbicie na szczegóły powoduje, że powstał trzeci wymiar tablicy.

0

Nie bardzo rozumiem Twoj problem, mowiac szczerze. Oprocz tego, ze dalej nie widze potencjalnych komplikacji i tego, co chcesz osiagnac. Robilem pare razy spore harmonogramy na duzo wiecej osob (2 tys. osob w firmie) porozdzielanych na teamy, oddzialy, itp. I sql nadaje sie do tego wysmienicie.

Wracajac do struktury:

  1. Co oznacza tabela ScheduleCodes?
  2. Gdzie jest data dotyczaca danego dnia pracy?
  3. Czemu nie oznaczac poczatku i konca pracy godzinami (tak jak podalem w przykladzie)? W ten sposob moglbys 'wycinac' godziny poza praca oznaczajac je np. w osobnej tabeli (tabela urlopy czy cos). O ile sie nie myle w Northwindzie (przykladowa baza na sql 2000) byl przyklad tabel pracowniczo-urlopowych.

PS. Jesli nie mozesz tego ogarnac sql'em to tym bardziej pakowanie to w twarda strukture binarna uniemozliwi Ci gladkie zmiany w przyszlosci.

0

Muszę się przespać z tym problemem. Johny_bravo wiem do czego zmierzasz. Nothwind nieco mi rozjaśnił. Musze pokombinować z tym. Niebawem się odezwę z szablonem.

Nie poradzę sobie z zapytaniami INSERT i UPDATE. Gdybym miał tabelę

CREATE TABLE Employee(
  LastName VARCHAR (
  Day1 VARCHAR(5),
  (...)
  Day31 VARCHAR(5)
);

To wszystko ok. Problem w tym, że przy takiej strukturze SQL jest do d*** bo SELECT działa na wierszach a nie kolumnach - czyli muszę dokonać transpozycji do takiej postaci. Do tego dochodzi, że jak kliknę komórkę na DataGrid-Master to w DataGrid-Detail pojawia się szczegółowa lista. Problem mam w odwzorowaniu współrzędnych. komórki na rzeczywisty wpis w bazie. Bo pic polega na tym, ze bardzo istotnym elementem struktury jest odwzorowanie kalendarza pracy. W momencie gdy mam zwykłą tablicę dwuwymiarową to nałożenie na to DataGrid jest proste. Potem podczas zapisu nadpisanie całości tablicy. Bo z SQL to skąd będę wiedział co było modyfikowane.

Johny_bravo, pisałeś, że robiłeś już takie harmonogramy - czy miały one taka formę? Jak rozwiazałeś inserty i updaty do bazy?
Poniżej wklejam większe zdjęcie: http://www.fotosik.pl/showFullSize.php?id=292d1d2e4f62e047

Na rapida wklejam plik harmonogram.xls
http://rapidshare.com/files/172931390/Harmonogram_2008.xls.html

I jeszcze jedno. Nie miałbym problemu gdyby:
Tabela1(pracownik) - Tabel2(kalendarz) - Tabela3(szczegóły dnia). Ale muszę pokazać wszystko na raz.

0

Widok?

0

Ok, zerkne wieczorkiem, dzis albo jutro. Masz do tego jakas chocby skrocona legende?

0

.Name = "1" 1 zmiana
.Name = "2" 2 zmiana
.Name = "UW" urlop wypoczynkowy
.Name = "UO" opieka na dziecko
.Name = "UZ" urlop na żadanie
.Name = "UB" urlop bezpłatny
.Name = "NN" nieobecny nieusprawiedliwiony
.Name = "NU" nieobecny usprawiedliwony
.Name = "L4" chorobowe
.Name = "OP"
.Name = "D" delegacja
.Name = "UM" macieżyński
.Name = "UD" wychowawczy
.Name = "SR" świadczenie rehabilitacyjne
.Name = "PW" płatne - wolne
.Name = "R" dzień wolny od pracy
.Name = "UT" wstępnie planowany urlop
.Name = "SP" spóźnienie

To są dostępne króty. Makro działa na zasadzie: ilość (np) UW * domyślny system pracy (12/8 h).
Zapis 10+2 oznacza 10 godzin w dzień i w nocy.

0

Co powiesz na to?

CREATE TABLE [dbo].[WorkTypes](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Hours] [int] NOT NULL,
 CONSTRAINT [PK_WorkTypes] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

CREATE TABLE [dbo].[WorkDays](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Date] [datetime] NOT NULL,
	[DayHours] [int] NULL,
	[NightHours] [int] NULL,
	[HolidayTypeID] [int] NULL,
	[HolidayHours] [int] NULL,
	[EmployeeID] [int] NOT NULL,
 CONSTRAINT [PK_WorkDays] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

CREATE TABLE [dbo].[Teams](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nchar](1) NOT NULL,
 CONSTRAINT [PK_Teams] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

CREATE TABLE [dbo].[HolidayTypes](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Type] [nchar](10) NOT NULL,
 CONSTRAINT [PK_HolidayTypes] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

CREATE TABLE [dbo].[Employees](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[TeamID] [int] NOT NULL,
	[AreaID] [int] NOT NULL,
	[WorkTypeID] [int] NOT NULL,
	[EmployeeNo] [int] NOT NULL,
	[FirstName] [nvarchar](50) NOT NULL,
	[LastName] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

CREATE TABLE [dbo].[Areas](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nchar](3) NOT NULL,
 CONSTRAINT [PK_Areas] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)

Zapytanie wybierajace dane pracownikow:

select
	t.name, 
	a.name,	
	wt.hours, 
	employeeno, 
	lastname, 
	firstname
from employees e 
	inner join teams t on t.id = teamid
	inner join areas a on a.id = areaid
	inner join worktypes wt on wt.id = worktypeid

Zapytanie pobierajace jeden wiersz z godzinami dla danego miesiaca i pracownika

select
	date, 
	cast (dayhours as varchar) + case when nighthours is null then '' else '+' + cast (nighthours as varchar) end hours, 
	case when holidaytypeid is null then '' else ht.type + case when holidayhours is null then '' else '-' + cast (holidayhours as varchar) end end as holidayhours
from workdays
	left join holidaytypes ht on ht.id = holidaytypeid
where 
	employeeid = 15
	and year(date) = 2008
	and month(date) = 5
order by
day(date)

Oczywiscie obydwa zapytania mozna polaczyc. Czyli pobierac dane pracownikow jednym zapytaniem, drugim pobierac caly miesiac dla wszystkich i laczyc po stronie aplikacji w jedna tabele po id pracownika - tak jak jest teraz w excelu.

Oczywiscie tabele powinny byc polaczone kluczami obcymi, ktorych tu nie zamieszczalem, zeby nie zaciemniac obrazu. Dane godzinowe mozna pobierac rozlacznie, czyli kazda kolumne osobno i laczyc po stronie aplikacji (np. godziny_dzienne+godziny_nocne). Daje to wieksza mozliwosc manipulacji warstwa prezentacji danych - w przypadku zmiany sposobu wyswietlania danych.

Jak widac w ten sposob inserty sa banalne - wystarczy dodac odpowiedni wpis w tabeli workhours i juz. Sam sposob dodawania mozna w wiekszosci zrealizowac przez comboboxy, wybierajac wartosci np. urlopu z odpowiednich tabel. W razie przybycia innych rodzajow wystarczy uzupelnic tabele slownikowe.

Oczywiscie to propozycja na podstawie tego co napisales (wynik wizualny ma byc ten sam). Daj znac czy taka struktura zalatwia Twoje problemy.

PS. Z tym makro nie zalapalem, oofice chyba mi nic nie pokazal.

PS2. Moge na maila przeslac baze z wygenerowanymi 400 tys. rekordow do testow.

0

Makra tworzą Ci pasek w menu (o ile masz średni poziom zabezpieczeń). Co do schematu - podoba mi się. Muszę tylko to ubrać w jakiś interfejs i przetestować czy ergonomia mi pasuje. Dziękuję za to odezwę się niebawem i powiem ja poszło

0

Ten z danym miesiacem roku? Jesli tak, to nie ma problemu :)

0 użytkowników online, w tym zalogowanych: 0, gości: 0