Ankete i testovi

Thursday, 25.09.2008 – Zidar

 Automatska obrada podataka pocela je kada je gospodin po imenu Herman Hollerith za potrebe americke vlade obradio prikupljene podatke o popisu stanovnistva. Tada jos nije bilo kompjutera, sve se desava davne 1890. godine.  Gospodin Hollerith je zatim osnovao firmu pod imenom Inetrnational Business Machines, dobro nam poznati IBM.

Za ljubitelje istorije, evo interesantan link:
 http://wiki.answers.com/Q/When_did_herman_hollerit_invented_the_tabulating_machine

I naravno wikipedia clanak  http://en.wikipedia.org/wiki/Herman_Hollerith

Tema ovog posta nije Herman Hollerith, niti IBM. Tema je obrada anketa i upitnika na racunarima,  odnosno primena relacionih baza podataka . Kako se generalno resavaju ovakvi problemi?  Postoje naravno bar dva moguca pristupa – relacioni i nerelacioni. Pokazacemo da  nerelacioni pristup izgleda u pocetku mnogo laksi, ali se znacajnio komplikuje cim se odmaknemo od unosa podataka. Relacion nacin moze da bude zbunjujuci na pocetku i nesto je tezi za unos podataka, ali se kasnije stvari znatno lakse resavaju od nerelacionog nacina. Pokazacemo oba nacina, da se vide prednosti i mane na obe strane.

Pretpostavimo se da nam je neko narucio obradu ankete.  Podaci se prikupljaju, mi ih spakujemo nekako u kompjuter i vratiom naruciocu obradjene. Anketa/upitnik se sastoji od pitanja koja nude vise odgovora, a bira se tacno jedan.  Treba isporuciti tabelu/izvestaj gde su prikazane frekvencije izabranih odgovora. Za svako pitanje treba utvrditi koji odgovor je koliko puta izabran.

Anketa ili upitnik

Uzmimo najprostiji moguci slucaj. Dat je anketni listic/upitnik sa odredjenim brojem pitanja. Ucesnici ankete odgovaraju na pitanja zaokruzivanjem jednog od ponudjenih odgovora. Sva pitanja su tipa “zaokruzi jednu od ponudjenih opcija”. Da pojednostavimo problem, broj ponudhjenih opcija ce uvek biti cetiri  i opcije su ‘a’,’b’,’c’,’d’.  Moramo da dodamo jos dva znaka. Jedan za  slucaj kada je izbarano vise od jedneog odgovora i kada nije izabrano nista. Neka to budu ‘#’ za vise od jednog odgovora i ‘-‘ (minus) za slucaj kada nije izabrano nista.

Process prikupljanja odgovora i pretvaranje odgovora u elektronski oblik u danasnje vreme ide obicno ovako:

Odstampaju se listovi sa pitanjima. Svaki se list oznaci jedinstvenim brojem, koji je prikazan u vidu barkoda. Listovi se podele ucesnicima. Ucesnici oznace izabrane odgovore. Ucesnici vrate listove.  Ako ucesnika ima malo, sve se moze ukucati u nekakvu bazu podataka. Ako ucesnika ima mnogo, mogu se listovi provuci kroz skenere. Skeneri ‘znaju’ odakle da citaju odgovore i  na kraju izbace tekst fajl gde je svaki red jedan upitnik, a odgovori su polja u tom redu, onicno odvojena zarezom. Pitanje je – sta dalje? Drugim recima, kako treba da izgleda baza podataka u koju cemo da a) ukucamo podatke ili b) smestimo podatke koje je procitao skener. Cesto imamo kobinaciju oba metoda – vecina listova ide kroz skenere, a zakasneli i zaostali odgovori unose se rucno. Sta da se radi?

Prva ideja koja ljudskim bicima pada na pamet je da se napravi tabela koja ima jednu kolonu za identifikacioni broj lista, to bi bio ujedno i primarni kljuc, i po jednu kolonu za odgovore. Vrlo elegantno na prvi pogled.  Napraviti formu za unos u ovakvu tabelu je zaista trivijalno, u nekakvom Accesu ili VB ili bilo cemu drugom. Import iz tekstualnih fajlova koje dobijemo od skenera je veoma jednostavan. Na prvi pogled. Tabela bi mogla da izgleda ovako:

ID Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q8 Q10 Q11 Q12
1 a b c a a b d c d a b d
2 b c d a a c d a b c b a
3 c D c a a d c d a c c c

Sta ne valja? Prvo, kako obezbediti da se unose samo dozvoljene vrednosti?  Jednostavno, u dizajnu tabele stavite za svaku kolonu da su dozvoljene vrednosti samo a,b,c,d.  Ej, ali to moramo da uradimo za svaku kolonu, dakle  jos 11 puta. Nije strasno, ukoliko se setimo toga odmah. Iz iskustva znam da kad se izabere resenja sa ovakvom tabelom, ljudi su toliko ushiceni sto je tabela elegantna i ceo posao lak za programiranje, da zaborave na sve ostalo, pa se ova ogranicenja nikad ne postave…

Znaci, ovako bismo kreirali tabelu i uneli test podatke:

IF OBJECT_ID('tempdb..#Anketa') IS NOT NULL DROP TABLE #Anketa
GO
CREATE TABLE #Anketa
(
ID INT PRIMARY KEY
, Q1 VARCHAR(1) NOT NULL CHECK (Q1 IN ('a','b','c','d','#','-'))
, Q2 VARCHAR(1) NOT NULL CHECK (Q2 IN ('a','b','c','d','#','-'))
, Q3 VARCHAR(1) NOT NULL CHECK (Q3 IN ('a','b','c','d','#','-'))
, Q4 VARCHAR(1) NOT NULL CHECK (Q4 IN ('a','b','c','d','#','-'))
, Q5 VARCHAR(1) NOT NULL CHECK (Q5 IN ('a','b','c','d','#','-'))
, Q6 VARCHAR(1) NOT NULL CHECK (Q6 IN ('a','b','c','d','#','-'))
, Q7 VARCHAR(1) NOT NULL CHECK (Q7 IN ('a','b','c','d','#','-'))
, Q8 VARCHAR(1) NOT NULL CHECK (Q8 IN ('a','b','c','d','#','-'))
, Q9 VARCHAR(1) NOT NULL CHECK (Q9 IN ('a','b','c','d','#','-'))
, Q10 VARCHAR(1) NOT NULL CHECK (Q10 IN ('a','b','c','d','#','-'))
, Q11 VARCHAR(1) NOT NULL CHECK (Q11 IN ('a','b','c','d','#','-'))
, Q12 VARCHAR(1) NOT NULL CHECK (Q12 IN ('a','b','c','d','#','-'))
)
GO
INSERT INTO #Anketa 
VALUES (1, 'a', 'b', 'c', 'a', 'a',  'b', 'd', 'c', 'd', 'a', 'b', 'd')
INSERT INTO #Anketa 
VALUES (2, 'c', 'a', 'a', 'a', 'a',  'b', 'd', 'c', 'c', 'a', '-', 'c')
INSERT INTO #Anketa 
VALUES (3, 'c', 'c', 'c', 'a', 'a',  'a', 'd', 'c', 'a', 'a', 'a', 'd')
INSERT INTO #Anketa 
VALUES (4, 'a', 'd', 'c', 'a', 'a',  'b', 'd', 'a', 'b', 'b', 'a', 'c')
INSERT INTO #Anketa 
VALUES (5, '#', 'a', 'c', 'a', 'a',  '-', 'd', 'c', 'd', 'a', 'b', '-')
INSERT INTO #Anketa 
VALUES (6, 'a', 'b', 'c', 'a', 'a',  'a', 'a', 'b', '-', 'a', '#', '-')
INSERT INTO #Anketa 
VALUES (7, 'b', 'c', 'c', 'a', 'a',  'c', 'd', 'c', 'c', 'c', 'c', '-')
INSERT INTO #Anketa 
VALUES (8, 'c', 'd', 'c', 'a', 'c',  'b', 'd', 'c', 'b', 'a', 'b', '#')
INSERT INTO #Anketa 
VALUES (9, '-', '#', 'c', 'a', 'a',  'b', 'd', 'd', '#', 'a', 'a', 'd')
INSERT INTO #Anketa 
VALUES (10, 'a', 'c', 'c', 'a', 'a', 'c', 'd', 'c', 'a', '#', '-', '#')
INSERT INTO #Anketa 
VALUES (11, 'a', 'b', 'c', 'a', 'a', 'c', 'd', 'c', 'd', 'a', 'b', 'd')
INSERT INTO #Anketa 
VALUES (12, 'c', 'a', 'a', 'a', 'a', 'b', 'd', 'c', 'c', 'a', '-', 'c')
INSERT INTO #Anketa 
VALUES (13, '#', 'a', 'c', 'a', 'a', '-', 'd', 'c', 'd', 'a', 'b', '-')
INSERT INTO #Anketa 
VALUES (14, 'c', 'c', 'c', 'a', 'a', 'a', 'd', 'c', 'a', 'a', 'a', 'd')
INSERT INTO #Anketa 
VALUES (15, '#', 'a', 'c', 'a', 'a', '-', 'd', 'c', 'd', 'a', 'b', '-') 

 Recimo da je sve u redu, niko nije uneo sta ne sme da se unese, podaci su ‘cisti’. Kako cemo da ih obradimo? Ono sto se trazi za anketu jeste ‘za svako pitanje, koliko ljudi je odgovorili a,b,c, ili d i koliko ih je zaokruzilo dva i vis eodgovora ili nije izabralo ni jedan. To se zove ‘izracunati frekvencije za pitanja ankete’. Elem, za pitanje Q1 imali bismo:

SELECT Q1, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1 

Q1   Freq
---- -----------
#    3
-    1
a    5
b    1
c    5  

 

Za pitanje Q2 bilo bi:

SELECT Q2, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q2 

Q2   Freq
---- -----------
#    1
a    5
b    3
c    4
d    2 

(5 row(s) affected) 

I tako za sva ostala pitanja, njih 12 ukupno. Ako malo dalje pogledam rezultate, vidim da je naziv prve kolone ujedno i naziv pitanja. Zasto ovo ne valja? Pa ako hocu da vidim sva pitanja odjednom, mogu recimo da uradim UNION kveri, ovako:

SELECT Q1, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
UNION
SELECT Q2, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q2
GO
Q1   Freq
---- -----------
#    1
#    3
-    1
a    5
b    1
b    3
c    4
c    5
d    2 

(9 row(s) affected) 

Rezultat je necitak. Ne zna se sta je sta? A ako probate UNION ALL dobicete 10 reodova u rezultatu umesto 9. Znaci, nije bas tako jednostavno.   Ajde da napravimo da radi:

SELECT 'Q1' AS Pitanje, Q1 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
UNION 
SELECT 'Q2' AS Pitanje, Q2 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q2
GO
Pitanje Odgovor Freq
------- ------- -----------
Q1      #       3
Q1      -       1
Q1      a       5
Q1      b       1
Q1      c       5
Q2      #       1
Q2      a       5
Q2      b       3
Q2      c       4
Q2      d       2 

(10 row(s) affected)

 Sada se lepse vidi sta je sta, naziv pitanaj je u rezultatu kverija. Mali problem je sto treba prosiriti UNION, na svih 12 pitanja. Sta ce biti kad bude 200 pitanaj u anketi?

Jos jedna stvar ne valja, a ne mora da se vid na prvi pogled. Na pitanje Q1 niko nije odgovorio ‘d’. stoga se ‘d’ nije pojavilo u rezultatu. Ne moze da se GROUP BY po necemu cega nema. Nazalost,  narucioci posla hoce da vide da je na pitanje Q1 0 (nula) ucesnika izabralo opciju ‘d’. Sad ce kveri da se zaista zakomplikuje. Kako da dobijete a,b,c,d,#,= na levoj strani? To mirise na nekakav LEFT JOIN. Dobro, LEFT JOIN – ali sa cim? Jedina tabela koju imamo jeste Anketa. Bas zato je resenje elegantno. I programiranje lako, i unos jednostavan. Do ovog momenta.

Sad uvidjamo da bi nam dobro dosla jos jedna tabela, da sadrzi moguce odgovore. Lako cemo dodati jos jednu tabelu za odgovore. Malo je doduse nepravilna praksa da se tabele dodaju u momentu obrade podataka. Ankete smo stampali pre dva meseca, skupljali ih i skenirali jos mesec dana, znaci projekat je poceo pre tri meseca. I programer koga smo zaposlili na ugovor da nam uradi key-entry aplikaciju je odavno otisao. Sta cemo sad? Pa da napravimo jos jednu tabelu, ko te pita za pravilnu praksu, daj da odradimo posao:

IF OBJECT_ID('tempdb..#MoguciOdgovori') IS NOT NULL DROP TABLE #MoguciOdgovori
GO
CREATE  TABLE #MoguciOdgovori  
(
MoguciOdgovor  VARCHAR(1) PRIMARY KEY
, Opis VARCHAR(50) NOT NULL
)
GO
INSERT INTO #MoguciOdgovori VALUES ('a','Izabran prvi odgovor')
INSERT INTO #MoguciOdgovori VALUES ('b','Izabran drugi odgovor')
INSERT INTO #MoguciOdgovori VALUES ('c','Izabran treci odgovor')
INSERT INTO #MoguciOdgovori VALUES ('d','Izabran cetvrti odgovor')
INSERT INTO #MoguciOdgovori VALUES ('#','Izabrano vise odgovore')
INSERT INTO #MoguciOdgovori VALUES ('-','Nije izabran ni jedan odgovor')  

Kako nam ovo pomaze? PA mozemo da napisemo nesto ovako:

SELECT M.MoguciOdgovor, Pitanje,  Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q1' AS Pitanje, Q1 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
) AS Q
ON M.MoguciOdgovor = Q.Odgovor
GO
MoguciOdgovor Pitanje Freq
------------- ------- -----------
#             Q1      3
-             Q1      1
a             Q1      5
b             Q1      1
c             Q1      5
d             NULL    NULL 

(6 row(s) affected)

Dobili smo ‘d’ ali imamo na dva mesta NULL koji treba da eliminisemo.  Trazi se da pise 0 (nula) a ne NULL za pitanja na koja niko nije odgovorio. I ne sme ostati NULL u koloni Pitanje. Kad napravimo UNION za svih 12 pitanja, nece se znati koji NULL pripada kom pitanju. Upit se dodatno komplikuje.

NULL u koloni Freq se lako resava upotrebom funkcije Coalesce, a naziv pitanja mozemo prosto da unesemo kako kostantu:

SELECT 
   'Q1' AS Pitanje
   , M.MoguciOdgovor
   ,  COALESCE(Freq,0) AS Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q1' AS Pitanje, Q1 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
) AS Q
ON M.MoguciOdgovor = Q.Odgovor 

Pitanje MoguciOdgovor Freq
------- ------------- -----------
Q1      #             3
Q1      -             1
Q1      a             5
Q1      b             1
Q1      c             5
Q1      d             0 

(6 row(s) affected) 

 

Sada naravno ovo treba da uradimo za ostalih 11 pitanja.  Sad vec postoji znacajna verovatnoca da ce se negde napraviti greska u Cut/Paste operacijama. U svakom slucaju, posle nekoliko proba/greski dobicemo nesto ovako:

SELECT 
   'Q1' AS Pitanje
   , M.MoguciOdgovor
   ,  COALESCE(Freq,0) AS Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q1' AS Pitanje, Q1 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
) AS Q
ON M.MoguciOdgovor = Q.Odgovor
UNION ALL
SELECT 
   'Q2' AS Pitanje
   , M.MoguciOdgovor
   ,  COALESCE(Freq,0) AS Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q2' AS Pitanje, Q2 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q2
) AS Q
ON M.MoguciOdgovor = Q.Odgovor 

Pitanje MoguciOdgovor Freq
------- ------------- -----------
Q1      #             3
Q1      -             1
Q1      a             5
Q1      b             1
Q1      c             5
Q1      d             0
Q2      #             1
Q2      -             0
Q2      a             5
Q2      b             3
Q2      c             4
Q2      d             2 

Za svih 12 pitanja, skripta bi bila 6 puta duza. Milina jedna za trazenje gresaka i kontrolu kvaliteta. Sta bi bilo za 200 pitanja?

Sta ce biti ako narucilac posla pozeli da vidi podatke pivotizovano, tako da svako pitanje bude u jednom redu a ab,c,d,#,- budu kolone? Dakle, kako dobiti ovakav rezultat:

Pitanje    A   B   C   D   #   -
----------------------------------
Q1         5   1   5   0   3   1
Q2         5   3   4   2   1   0 

Postoji li elegantan nacin da ovakav rezultat obezbedi SQL? Naravno da moze da se napise kod ADO/DAO, SQL kursor i slicno, ali moze li pomocu SQL upita? Naravno da moze. Ali nije jednostavno, kveri postaje zaista veliki. Jedna varijanta je ova:

WITH Frekvence AS
(
SELECT 
   'Q1' AS Pitanje
   , M.MoguciOdgovor
   ,  COALESCE(Freq,0) AS Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q1' AS Pitanje, Q1 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q1
) AS Q
ON M.MoguciOdgovor = Q.Odgovor
UNION ALL
SELECT 
   'Q2' AS Pitanje
   , M.MoguciOdgovor
   ,  COALESCE(Freq,0) AS Freq
FROM #MoguciOdgovori AS M
LEFT JOIN
(
SELECT 'Q2' AS Pitanje, Q2 AS Odgovor, COUNT(*) AS Freq
FROM #Anketa
GROUP BY Q2
) AS Q
ON M.MoguciOdgovor = Q.Odgovor
)
SELECT 
   Pitanje
   , a = SUM(CASE WHEN MoguciOdgovor = 'a' THEN Freq ELSE 0 END)
   , b = SUM(CASE WHEN MoguciOdgovor = 'b' THEN Freq ELSE 0 END)
   , c = SUM(CASE WHEN MoguciOdgovor = 'c' THEN Freq ELSE 0 END)
   , d = SUM(CASE WHEN MoguciOdgovor = 'd' THEN Freq ELSE 0 END)
   , '#' = SUM(CASE WHEN MoguciOdgovor = '#' THEN Freq ELSE 0 END)
   , '-' = SUM(CASE WHEN MoguciOdgovor = '-' THEN Freq ELSE 0 END)
FROM Frekvence
GROUP BY Pitanje
GO
Pitanje a           b           c           d           #           -
------- ----------- ----------- ----------- ----------- ----------- -----------
Q1      5           1           5           0           3           1
Q2      5           3           4           2           1           0 

(2 row(s) affected)

Za svih 12 pitanja deo pod WITH klauzulom bio bi sest puta duzi. Sat bi bilo za 200 pitanja, ne smem ni da mislim. Deo posle WITH je interesantan. To je jedan nacin da se u T-SQL uradi pivotizacija tabele (crosstab kveri u Accessu)

Sve u svemu, nekako dodjosmo do rezultata koji bi trebao da zadovolji narucioca posla. Malo mukotrpno, ali sta se moze. Verovatno pogadjate da postoji bolji nacin da se ovo uradi. Postoji, ali necu ga pokazati u ovom momentu. Prikazani upiti mogu nekume da daju ideju da tabela koja sadrzi jedan red po anketnom listu zaista moze da funkcionise. Veoma slicnu situaciju imamo sa testiranjem ucenika i studenata.

 

Skolski test = anketa sa tacnim odgovorima

Zamislite da je u pitanju skolski test. Tada se ne traze frekvencije odgovora (traze se , ali nije primarno). Trazi sa da vidimo koliko tacnih odgovora ima svaki ucesnik testa.  Na osnovu broja tacnih odgovora odredjuje se ocena.

Test izgleda isto kao i anketa. Ponudjeno je vise odgovora,  treba zaokruziti onaj za koji mislite da je tacan. Za svakog ucesnika, odgovor na svako pitanje se poredi sa tacnim odgovorom i rezultat pamti. Kad se prodje kroz sva pitanja, prebroji se koliko ima tacnih odgovora. Na osnovu broja tacnih odgovora, daje se ocena.

Mozemo da koristimo obe tabele iz prethodnog primera. Kako napisati upit  koji broji tacne odgovore? Ako bismo pisali program, pseudo kod bi isao otprilike ovako:

Za svaki red u tabeli Ankete
  UcesnikTesta = Ankete .ID
  BrojTacnihOdgovora = 0
  If Ankete .Q1 = ‘c’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q2 = ‘a’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q3 = ‘a’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q4 = ‘b’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q5 = ‘d’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q6 = ‘c’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q7 = ‘b’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q8 = ‘c’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q9 = ‘a’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q10 = ‘c’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q11 = ‘a’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1
  If Ankete .Q12 = ‘b’ then BrojTacnihOdgovora= BrojTacnihOdgovora+1 
  Upisi (UcesnikTesta ,BrojTacnihOdgovora) u tabelu RezultatiTesta
  Predji na novi red

Lepo.  Hard-kodirali smo tacne odgovore. Nije bas najbolja praksa, ali radi. Vecina aplikacija koja resava problem testova radi bas ovako. Koliko testova, toliko i aplikacija. Ili barem procedura za brojanje tacnih odgovora.  Milina jedna za trazenje gresaka i kontrolu kvaliteteta. Mozemo li dakle da napisemo upit? Mozda ovako (samo za prvih 6 pitanja, tek da vidimo ideju) :

WITH OdgovoriNaPitanja AS
(
SELECT Ucesnik = ID, Pitanje = 'Q1', TacanOdgovor = CASE WHEN Q1='c' THEN 1 ELSE 0 END
FROM #Anketa
UNION
SELECT Ucesnik = ID, Pitanje = 'Q2', TacanOdgovor = CASE WHEN Q2='a' THEN 1 ELSE 0 END
FROM #Anketa
UNION
SELECT Ucesnik = ID, Pitanje = 'Q3', TacanOdgovor = CASE WHEN Q3='a' THEN 1 ELSE 0 END
FROM #Anketa
UNION
SELECT Ucesnik = ID, Pitanje = 'Q4', TacanOdgovor = CASE WHEN Q4='b' THEN 1 ELSE 0 END
FROM #Anketa
UNION
SELECT Ucesnik = ID, Pitanje = 'Q5', TacanOdgovor = CASE WHEN Q5='d' THEN 1 ELSE 0 END
FROM #Anketa
UNION
SELECT Ucesnik = ID, Pitanje = 'Q6', TacanOdgovor = CASE WHEN Q6='c' THEN 1 ELSE 0 END
FROM #Anketa
)
SELECT Ucesnik, COUNT(TacanOdgovor) AS BrojTacnihOdgovora
FROM OdgovoriNaPitanja
WHERE TacanOdgovor = 1
GROUP BY Ucesnik
GO
Ucesnik     BrojTacnihOdgovora
----------- ------------------
2           3
3           1
5           1
7           1
8           1
10          1
11          1
12          3
13          1
14          1
15          1 

(11 row(s) affected)

Lepo, ali mukotrpno. I podlozno greskama.  Svi znamo da nije dobra ideja da se podaci ugradjuju u programski kod. A mi smo upravo to uradili. A to nije ni dobro programiranje ni dobra upotreba SQL-a. A nedostaju i neki ucesnici testa – oni koji nemaju ni jedan odgovor. DObro, LEFT JOIN iz sada znate i sami da postavite….

Narucioci posla imaju obicaj da daju pogresne podatke o tacnim odgovorima, pa saznamo u zadnji cas da tacan odgovor na pitanje 5 nije ‘d’ nego ‘b’. A mi vec odradili kod, napunili tabelu rezultata. Sad treba da menjamo kod. U brzini, programer umesto pitanja 5 promeni pitanje 6….

U nastavku, pokusacu da pokazem kako se ovaj problem moze resiti relaciono. Pa da uporedimo resenja. Za najprostiji slucaj.

🙂

  1. 4 Responses to “Ankete i testovi”

  2. Eeee svaka cast Zidar – veoma koristan i poucan tekst!

    Sto se tice problema sa naknadnm izmjenama podataka, za tu svrhu bi se mogla koristiti neka lookup tabela, koja bi sadrzavala poveznicu izmedju pitanja i odgovora…

    By Dejan on Sep 26, 2008

  3. Havla 🙂
    Ovo je us tvari bilo kako NE TREBA raditi, iako je moguce raditi i ovako. U pravu si za lookup tabelu, ali to je razmisljanje na visem nivou. Sledeci post bice o tome – kako u stvari treba raditi.

    Nego, zasto danas kod izgleda mnogo lepse nego juce kad sam ga postovao? Da li ga ti formatiras naknadno? Ako da, hvala ti od srca. Ako mi kazes kako se formatira, ustedecemo tvoj trud i vreme, a ja cu vise da pisem. Pravo da ti kazem, plasi me editor i najgore mi je da prenesem kod. Potrosim vise vremena na te tehnicke stvari nego na stvarno pisanje. Valjda sam prestar pa mi teko ide da nuacim tehnicke stvari potrebna za blogovanje.

    🙂

    By Zidar on Sep 26, 2008

  4. @Zidar: Da, ja sam formatirao SQL upite naknadno, radi bolje preglednosti, a i ljepse izgleda. 🙂
    Znas kako ja radim – tekst pisem normalno u Visual rezimu, a SQL upite prvo napisem u Notepadu, pa onda na sajtu iz Visual rezima predjem u HTML rezim i copy/pasteujem taj SQL kôd izmedju <pre clas=”sql” name=”code”> i </pre>tagova.

    Znaci, ovako npr.:
    <pre clas=”sql” name=”code”>
    SELECT neka_kolona
    FROM neka_tabela
    WHERE druga_kolona = ‘SQL formatiranje’;
    </pre>

    By Dejan on Sep 26, 2008

  5. Dejane, puno hvala. Naravno da lepse izgleda. Potrudicu se da naucim ovo i da ti ustedim vreme u buducnosti.

    🙂

    By Zidar on Sep 29, 2008

Post a Comment