Konwersja varchar do date

0

Cześć,
potrzebuję stworzyć funkcję, która jako parametr przyjmie datę, po to by potem w ciele funkcji wyciągnąć z bazy te rekordy, które będą odpowiadały miesiącowi podanymi w argumencie.
Czyli. Wywołuję funkcję np FUNKCJA(16/04) - czyli chodzi mi o kwiecień 2016 i potem chciałbym zamienić ten varchar na datę. Niestety to nie wychodzi.

CREATE OR REPLACE procedure rozkladWMiesiacu(miesiac char)
IS
test number;
a date:=TO_DATE(miesiac, 'yy/mm');
wiersz wydatki%rowtype;
cursor zapytanie IS SELECT * FROM wydatki  WHERE dat = a;
 
begin
dbms_output.put_line(miesiac);
dbms_output.put_line(a);
open zapytanie;
loop 
 
fetch zapytanie INTO wiersz;
exit when zapytanie%notfound;
dbms_output.put_line(wiersz.wyd_id ||' '||
wiersz.kat_id ||' '|| wiersz.kwota ||' '|| wiersz.czl_id ||' '|| wiersz.dat ||' '|| wiersz.nazwa);
dbms_output.put_line(wiersz.wyd_id);
dbms_output.put_line(wiersz.wyd_id);
 
end loop;
test:=2;
end; 

Być może kod jest mocno chaotyczny - dopiero zaczynam SQL-a ale nie rozumiem dlaczego nie działa. Zauważyłem, że ta instrukcja

dbms_output.put_line(a);

wyrzuca datę w formacie yy/mm/dd czyli ta konwersja

a date:=TO_DATE(miesiac, 'yy/mm');

nie zadziałał tak jak oczekiwałem.

Gdzie leży problem?

0
cursor zapytanie IS SELECT * FROM wydatki  WHERE to_char(dat, 'yy/mm') = miesiac;

typ date zawsze trzyma datę a data składa się z roku miesiąca i dnia (dodatkowo może być strefa czasowa)

0

No tak. To jest dla mnie jasne, tylko że mnie interesuje sam miesiąc. Być może moje rozumowanie jest błędne ale: skoro chcę wyciągnąć wszystkie rekordy z np. czerwca 2016 to interesują mnie wszystkie rekordy, których data wygląda tak: 16/06/xx gdzie xx jest dowolne. Wydaje mi się, że taki efekt uzyskam konwertując datę z rekordu do formatu 'yy/mm' . Tak jak powyżej. Stąd powyższy kod. Jeśli myślę źle to jak rozwiązać mój problem?

0
  1. myślisz źle - jak konwertujesz na datę ciąg 06/2016 to dostajesz datę 01.06.2016 - data to data nie da się w typie data zapisać czegoś innego niż data a ciąg 06/2016 (czy też 06/16) datą nie jest.
  2. dostałeś odpowiedź wyżej ale nawet nie sprawdziłeś - trzeba zamienić NIE string na datę ale datę na string i porównywać string. Innym rozwiązaniem jest zamiast porównania użycie BETWEEN
0

możesz również wyciąć z daty potrzebny ci zakres np.

cursor zapytanie IS SELECT * FROM wydatki WHERE extract(year from dat) = 2016 AND extract(month from dat) = 04;

Da to taki sam wynik jak rozwiązanie @abrakadaber

0

nudziło mi się :)

tabela

CREATE TABLE test_table (
  test_date DATE);

indeksy, można zrobić test bez i z

CREATE INDEX idx_test_table_date1 ON test_table (to_char(test_date, 'YYYY/MM'));
CREATE INDEX idx_test_table_date2 ON test_table (extract(year from test_date), extract(month from test_date));
CREATE INDEX idx_test_table_date3 ON test_table (test_date);

testowe dane

DECLARE
  i NUMBER;
BEGIN
  FOR i IN 1..1000000 LOOP
    INSERT INTO test_table(test_date) VALUES(TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '1800-01-01','J'),TO_CHAR(DATE '2100-12-31','J'))),'J'));
  END LOOP;
END;
/

procedura testowa

declare     
    c sys_refcursor;
    v_num_tab dbms_sql.number_table;    
    v_start_time number;
    v_end_time number;
begin
    v_start_time := dbms_utility.get_time;
    for i in 1..100000
    loop
        open c for select 1 from test_table WHERE extract(year from test_date) = 2016 AND extract(month from test_date) = 6;
        loop        
            fetch c bulk collect into v_num_tab limit 500;
            exit when c%notfound;    
        end loop;
        close c;
    end loop;
    v_end_time := dbms_utility.get_time;
    dbms_output.put_line('EXTRACT : ');
    dbms_output.put_line((v_end_time - v_start_time) / 100);
    
    v_start_time := dbms_utility.get_time;
    for i in 1..100000
    loop
        open c for select 1 from test_table WHERE to_char(test_date, 'YYYY/MM') = '2016/06';
        loop        
            fetch c bulk collect into v_num_tab limit 500;
            exit when c%notfound;    
        end loop;
        close c;
    end loop;
    v_end_time := dbms_utility.get_time;
    dbms_output.put_line('TO_CHAR : ');
    dbms_output.put_line((v_end_time - v_start_time) / 100);

    v_start_time := dbms_utility.get_time;
    for i in 1..100000
    loop
        open c for select 1 from test_table WHERE test_date BETWEEN To_Date('2016.06.01', 'yyyy.mm.dd') AND To_Date('2016.06.30', 'yyyy.mm.dd');
        loop        
            fetch c bulk collect into v_num_tab limit 500;
            exit when c%notfound;    
        end loop;
        close c;
    end loop;
    v_end_time := dbms_utility.get_time;
    dbms_output.put_line('BETWEEN : ');
    dbms_output.put_line((v_end_time - v_start_time) / 100);

end;
/

bez indeksów radzę zmienić w pętlach 100000 na 100 :)

wynik z indeksami:

Line Pos Text                                 
4        PL/SQL block, executed in 17.426 sec.
         EXTRACT :                            
         5,85                                 
         TO_CHAR :                            
         5,76                                 
         BETWEEN :                            
         5,8                                  
         Total execution time 17.442 sec.     
Line Pos Text                                 
4        PL/SQL block, executed in 17.274 sec.
         EXTRACT :                            
         5,85                                 
         TO_CHAR :                            
         5,69                                 
         BETWEEN :                            
         5,73                                 
         Total execution time 17.495 sec.     

wynik bez indeksów

Line Pos Text                                 
4        PL/SQL block, executed in 32.399 sec.
         EXTRACT :                            
         11,9                                 
         TO_CHAR :                            
         18,58                                
         BETWEEN :                            
         1,9                                  
         Total execution time 32.624 sec.     

W planach nie ma nic nadzwyczajnego
EXTRACT

-------------------------------------------------------------------------
| Id  | Operation        | Name                 | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                      |    93 |  2418 |     3 |
|   1 |  INDEX RANGE SCAN| IDX_TEST_TABLE_DATE2 |    93 |  2418 |     3 |
-------------------------------------------------------------------------

TO_CHAR

-------------------------------------------------------------------------
| Id  | Operation        | Name                 | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                      |  9335 | 46675 |     3 |
|   1 |  INDEX RANGE SCAN| IDX_TEST_TABLE_DATE1 |  9335 | 46675 |     3 |
-------------------------------------------------------------------------

BETWEEN

-------------------------------------------------------------------------
| Id  | Operation        | Name                 | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                      |   258 |  2322 |     3 |
|   1 |  INDEX RANGE SCAN| IDX_TEST_TABLE_DATE3 |   258 |  2322 |     3 |
-------------------------------------------------------------------------

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