Links

   Quran Explorer - Interactive Audio Recitations & Translations
Showing posts with label sql. Show all posts
Showing posts with label sql. Show all posts

Tuesday, February 18, 2014

ONE Trigger Function to rule them all


In many of my earlier applications/frameworks av always implemented table audit facility via java/python code...

granted this worked ok... unless the (malicious or otherwise) user accessed the apps DB via other means eg terminal, ide, et al...  

Recently av been doing this via DB triggers (...silence please.. i know this is not DB agnostic...) and had quite a challenge creating a 'universal' trigger function that will be used by all auditable tables...

The following is the solution, of course assembled from several blogs and sites and some fiddling on SQLFiddle......

Prerequisites (for those who wish to use this function as it is):
  • Audited table must capture a login id(bigint)
  • Must have an AFTER trigger calling this fxn
  • Table p_audit must be created in advance


CREATE OR REPLACE FUNCTION if_modified_func() returns TRIGGER AS $$
DECLARE
      pk        bigint;
      pk_name        varchar(100);
      pk_datatype    varchar(50);
BEGIN

    IF TG_WHEN <> 'AFTER' THEN
         RAISE EXCEPTION 'Audit Function may only run as an AFTER trigger';
    END IF;

    --first we identify the pk field of the db
    EXECUTE
    '(SELECT
        pg_attribute.attname, format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
      FROM pg_index, pg_class, pg_attribute
      WHERE pg_class.oid = ''' || TG_TABLE_NAME || '''::regclass
        AND indrelid = pg_class.oid
        AND pg_attribute.attrelid = pg_class.oid
        AND pg_attribute.attnum = any(pg_index.indkey)
        AND indisprimary)'
      INTO pk_name, pk_datatype;

    --TO GET record_id start here => http://www.sqlfiddle.com/#!15/64e41/2/0

    if TG_OP = 'UPDATE' then
        EXECUTE 'SELECT $1.'||pk_name INTO pk USING NEW;

        INSERT INTO p_audit (transaction_id, p_login_id, table_name, action, client_addr, client_port, client_query,
                  record_id, db_user, action_timestamp, data_before, data_after, updated_cols, narrative)
        VALUES(txid_current(), NEW.login_id, TG_TABLE_NAME, 'U', inet_client_addr(), inet_client_port(), current_query(),
                pk, CURRENT_USER, CURRENT_TIMESTAMP, hstore(OLD.*), hstore(NEW.*), akeys(hstore(NEW.*) - hstore(OLD.*)), NULL);

        return NEW;
    elsif TG_OP = 'DELETE' then
        EXECUTE 'SELECT $1.'||pk_name INTO pk USING OLD;

        INSERT INTO p_audit (transaction_id, p_login_id, table_name, action, client_addr, client_port, client_query,
                  record_id, db_user, action_timestamp, data_before)
        VALUES(txid_current(), OLD.login_id, TG_TABLE_NAME, 'D', inet_client_addr(), inet_client_port(), current_query(),
                pk, CURRENT_USER, CURRENT_TIMESTAMP, hstore(OLD.*));

        return OLD;
    elsif TG_OP = 'INSERT' then
        EXECUTE 'SELECT $1.'||pk_name INTO pk USING NEW;

        INSERT INTO p_audit (transaction_id, p_login_id, table_name, action, client_addr, client_port, client_query,
              record_id, db_user, action_timestamp, data_before, data_after, updated_cols, narrative)
        VALUES(txid_current(), NEW.login_id, TG_TABLE_NAME, 'I', inet_client_addr(), inet_client_port(), current_query(),
                pk,  CURRENT_USER, CURRENT_TIMESTAMP, NULL, hstore(NEW.*), NULL, NULL);

        return NEW;
    end if;
END;
$$ language plpgsql;


Finaly...
if you think this is coool....... just say it

=====
Graphically  u may present it like this

Thursday, November 21, 2013

Avoid 'SQL LIKE' - Use Full Text Search

Hello random googler,

Welcome back

Do u often find your self needing to do 'dirty' SQL LIKEs like the following;

SELECT member_id 
FROM vw_members
WHERE upper(member_name) LIKE 'JAY%' OR
upper(member_county) LIKE 'NAI%' OR
upper(staff_no) LIKE 'S01%' OR 
upper(mobile_no) LIKE '072%' OR 'i am tired of this stuff and this is only one permutation assuming the first 3 characters hav been filled...ouuuch'

? (yes question mark... this is where the question ends!!!)


If you answer is yes OR 'Y%'  ;-) ;-) then you are ready for FULL TEXT SEARCH

ONE TECHNIQUE...
Make your SQL VIEWS read for FTS by always having a column called 'ts_doc' that should contain the 'searchable' columns(of ur choice) ??????????


EG (postgres)

CREATE OR REPLACE VIEW vw_members AS
SELECT members.member_id, members.member_name, member.staff_no, members.mobile_no, andanyothercolumnyouwhishcoziassumeuknowsql,
 to_tsvector(COALESCE(members.member_name,'')||' '||COALESCE(member.staff_no,'') ||' '|| COALESCE(members.mobile_no,'')) as ts_doc


So that in your queries just become

SELECT blahblah
FROM vw_members
WHERE ts_doc @@ to_tsquery(' searchQuery:*') ";

NB: searchQuery is nothing but a cleanup to create a valid FTS query:-
searchQuery = searchQuery.replace(" ",":* & ") - in java, but of course u can use ur prefered technique... php, sql,

CONCLUSION
With the above u can use more than one search text(in whatever order or case) to narrow down the result..
for example with searchQuery like 'mik DS1 nai' it will search in all the columns (defined in ts_doc) for words starting with mik or DS1 or nai... thereby matching all entries for nairobi, all names starting with mik eg mike,mikel etc etc

NB: approach is 'begins with....'
(those who do LIKE '%xyz%' look for another evangelist !!!!)

example is in postgres

adios

Monday, June 10, 2013

Find foreign table using a given CONSTRAINT NAME


Ever tried to delete a record only to be told get ORA-02292
"integrity constraint (CONSTRAINT_NAME) violated - child record found"

How do you identify the table that is related to the constraint mentioned above. Of course if you explicitly name ur FK constraints(very unlikely) you have no problem.

ORACLE




SELECT owner, table_name
  FROM dba_constraints
 WHERE constraint_name = 'SYS_C0011582'


Friday, May 3, 2013

TRUE/REAL data pagination


I was recently implementing Lazy Loading in Primefaces

One of the insights i would like to share is that real efficient pagination is achieved by fetching data from the database (backend) in chunks as opposed to loading everything in memory.

This saves both bandwidth and memory (client side).

The trick here is to use OFFSET and LIMIT sql clauses in conjuction with ORDER BY.

Example 1: Returning the first 100 rows from a table called employee:

SELECT employee_id, employee_name, remarks FROM employee ORDER BY employee_id ASC LIMIT 100;

Example 2: Returning 10 rows from the table employee starting at row 15

SELECT employee_id, employee_name, remarks FROM employee ORDER BY employee_id ASC OFFSET 15 LIMIT 10;



Friday, April 19, 2013

Conditional Triggers in Postgres


Since version 9.0 we can execute triggers conditionaly

Example

CREATE TRIGGER trGenerateSchedule
AFTER INSERT OR UPDATE OF is_confirmed ON job_location
    FOR EACH ROW
    WHEN (NEW.is_confirmed = true)
    EXECUTE PROCEDURE generateSchedule();

This is a column trigger that listens for a change (to true) of a single column in the job_location table

Monday, January 24, 2011

XOR in Oracle

I had a situation where an XOR implementation was needed in a SELECT query. Since Oracle 10g doesnt have this operator predefined i did some gymnastics and ended up with this solution.

I decided to share this (among others) since human knowledge belongs to mankind

1. XOR in oracle

Example 1 - chars only

Eg we want to display results where ismale='1' XOR iscitizen='1' (ie either but not both)

Solution
SELECT ismale, iscitizen, (ismale + iscitizen) as xor
FROM tablename
WHERE ismale + iscitizen = 1;

NB: oracle will add(mathematical) chars as it would integers

Example 2 - varchars

Eg we want to display results where gender='male' XOR countryid='KE' (ie either but not both)

Solution
SELECT gender, countryid, (decode(gender,'male','1','0') + decode(countryid,'KE','1','0')) as xor
FROM tablename
WHERE (decode(gender,'male','1','0') + decode(countryid,'KE','1','0')) = 1;

NB: oracle will add chars as it would integers