Get most recent values of entry in database - ruby

I have a database built in Ruby using SQLite in the following way:
db.execute "CREATE TABLE IF NOT EXISTS Problems(ID INTEGER, stem BLOB NOT NULL, answer BLOB, datetime TEXT, lastmodifiedby TEXT, primary key (ID, datetime) )"
db.execute "INSERT INTO Problems VALUES(1, 'stem', 'answer', '12/26/2012 2:52:18 PM', 'bob')"
db.execute "INSERT INTO Problems VALUES(1, 'stem modified', 'answer', '12/26/2012 2:52:19 PM', 'bob')"
db.execute "INSERT INTO Problems VALUES(1, 'stem modified further', 'answer', '12/26/2012 2:52:20 PM', 'bob')"
The IDs for the first three entries are the same, however the times are different. I am currently using the following code to extract a single entry:
db = SQLite3::Database.new "#{dbname}"
stm = db.prepare "SELECT * FROM Problems WHERE ID=?"
stm.bind_param 1, id
rs = stm.execute
problem = rs.next
My first question - is there a way to condense the last 4 lines of code?
Second, when I select an entry from the Problems database, how would I add an option so that the most recent entry (in this case, the third one) is chosen?
And finally, how do I go about selecting all entries of a certain ID (here I only have the int 1, but in reality there are many others) so that I can output them as a string / write to a file, etc.
I have found answers to questions regarding most recent entry selection, but they seem quite complex. Would an ORDER BY work in some way?
Thanks for the help.

First of all, I think you have a data format problem. I don't think SQLite will understand '12/26/2012 2:52:18 PM' as a timestamp so you'll end up comparing your timestamps as strings. For example, if I add '12/26/2012 2:52:20 AM' to the mix, I get '12/26/2012 2:52:18 PM' and '12/26/2012 2:52:20 PM' as the lowest and highest values and that only makes sense if they're being compared as strings. Switch your data to ISO 8601 format so that you have these:
2012-12-26 14:52:18
2012-12-26 14:52:19
2012-12-26 14:52:20
and things will sort properly.
Once you have that fixed, you can use ORDER BY and LIMIT to peel off just one record:
stm = db.prepare('select * from Problems where ID = ? order by datetime desc limit 1')
rs = stm.execute(1)
problem = rs.next

Related

Function returning Last record

I don't often use ORACLE PL/SQL by the way but i need to understand what if anything in this function created by someone else
in the company before me is wrong as for it is not returning the latest record i've been told. I found out in some other forum issues that they
suggested to use the max(dateColumn) instead of "row_numer = 1" for example but not quite sure how to and where to incorporate that.
-- Knowing that --
We use Oracle version 12,
CustomObjectTypeA is an custom Oracle OBJECT TYPE defined by some old employee not longer in here,
V_OtherView is of Table_Mnd type beeing defined by some old employee not longer in here,
V_ABC_123 is a view created by some old employee not longer in here as well.
CREATE OR REPLACE FUNCTION F_TABLE_APPROVED (NUMBER_F_UPD number, NUMBER_F_GET VARcHAR2)
RETURN Table_Mnd
IS
V_OtherView Table_Mnd
BEGIN
SELECT CustomObjectTypeA (FromT.NUMBER_F,
FromT.OP_CODE,
FromT.CATG_CODE,
FromT.CATG_NAME,
FromT.CATG_SORT,
FromT.ORG_CODE,
FromT.ORG_NAME
FromT.DATA_ENTRY_VALID,
FromT.NUMBER_RECEIVED,
FromT.YEAR_1,
FromT.YEAR_2)
BULK COLLECT INTO V_OtherView
FROM (SELECT NUMBER_F,
OP_CODE,
CATG_CODE,
CATG_NAME,
CATG_SORT,
ORG_CODE,
ORG_NAME
DATA_ENTRY_VALID,
NUMBER_RECEIVED,
YEAR_1,
YEAR_2,
ROW_NUMBER() OVER (PARTITION BY BY ORG_CODE ORDER BY NUMBER_RECEIVED DESC, LOAD_DATE DESC) AS ROW_NUMBER
FROM V_ABC_123
WHERE NUMBER_F = NUMBER_F_UPD AND DATA_ENTRY_VALID <> 'OnGoing'
AND LOAD_DATE >= (SELECT sysdate-10 FROM dual)
AND LOAD_DATE <= (SELECT DISTINCT LOAD_DATE
FROM V_ABC_123
WHERE NUMBER_RECEIVED = NUMBER_F_GET)) FromT
WHERE FromT.ROW_NUMBER=1;
RETURN V_OtherView;
END F_TABLE_APPROVED;
The important bits of the query are:
SELECT ...
FROM (select ...,
ROW_NUMBER()
OVER (PARTITION BY ORG_CODE
ORDER BY NUMBER_RECEIVED DESC,
LOAD_DATE DESC) AS ROW_NUMBER
...) FromT
WHERE FromT.ROW_NUMBER = 1;
The "ROW_NUMBER" column is computed according to the following window clause:
PARTITION BY ORG_CODE
ORDER BY NUMBER_RECEIVED DESC, LOAD_DATE DESC
Which means that for each ORG_CODE, it will sort all the records by NUMBER_RECEVED,LOAD_DATE in descending order. Note that if the columns are Oracle DATEs, they will only be accurate to the nearest second; so if there are multiple records with date/times in the exact same 1-second interval, this sort order will not be guaranteed unique. The logic of ROW_NUMBER will therefore pick one of them arbitrarily (i.e. whichever record happens to be emitted first) and assign it the value "1", and this will be deemed the "latest". Subsequent executions of the same SQL could (in theory) return a different record.
The suspicious part is NUMBER_RECEIVED which sounds like it's a number, not a date? Sorting by this means that the records with the highest NUMBER_RECEIVED will be preferred. Was this intentional?
I'm not sure why the PARTITION is there, this would cause the query to return one "latest" record for each value of ORG_CODE that it finds. I can only assume this was intentional.
The problem is that the query can only determine the "latest record" as well as it can based on the data provided to it. In this case, it's possible the data is simply not granular enough to be able to decide which record is the actual "latest" record.

Same Function returning different results in Oracle

I run two queries and they are returning different results. Please help me find what am I missing.
select UPPER(TO_CHAR(Hire_date,'MONTH'))
from employees
returns a list of months(containing "2" entries for 'MARCH')
SELECT COUNT(EMPLOYEEID) "March Joinees"
FROM Employees
WHERE UPPER(TO_CHAR(Hire_date,'MONTH')) = 'MARCH';
returns 0 as count
to_char(<date>, 'Month') produces a string of length equal to the greatest length of any month name (in the current session's language) - it does so by padding with spaces. This is why the second query produces no results; if you wrap the upper.... within trim(...) it will work, but it would be better not to use to_char(..., 'Month') for this kind of query to begin with. I am sure you came to the same conclusion but you just want to know what is going on...
Here's an illustration. It may seem like the first column in the result is the string 'March'; however, the second column doesn't lie: the result in the first column is actually 'March ' (with four spaces at the end).
select to_char(date '2016-03-01', 'Month') as month_as_string,
length( to_char(date '2016-03-01', 'Month') ) as len
from dual
;
MONTH_AS_STRING LEN
--------------- ---
March 9
Then you may ask why Oracle made such a weird choice. That's a much tougher question. Anyway, this behavior is documented. http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34924 and scroll down to MONTH in table 2.15.
As stated by mathguy already, 'MONTH' makes padding with spaces. Try
SELECT '"'||TO_CHAR(Hire_date, 'Month')||'"' FROM employees
to see the effect. Use either function TRIM or Format Model Modifiers FM
Then result of TO_CHAR(Hire_date, 'Month') depends your current session NLS_DATE_LANGUAGE value. Other session may get "März" or "Μάρτιος". Specify either date language or use Month numbers.
Actually UPPER(TO_CHAR(Hire_date,'MONTH')) is redundant. Format MONTH returns month name in upper case, you don't have to make UPPER() again.
Taking all this into account you should use one of the expressions below
WHERE TO_CHAR(Hire_date, 'fmMONTH', 'NLS_DATE_LANGUAGE = english') = 'MARCH'
WHERE UPPER(TO_CHAR(Hire_date, 'fmMonth', 'NLS_DATE_LANGUAGE = english')) = 'MARCH'
WHERE TRIM(TO_CHAR(Hire_date, 'MONTH', 'NLS_DATE_LANGUAGE = english')) = 'MARCH'
WHERE TO_CHAR(Hire_date, 'MM') = '03'
WHERE EXTRACT(MONTH FROM Hire_date) = 3

Compare date to month-year in Postgres/Ruby

I have a date column in my table and I would like to 'filter'/select out items after a certain year-month. So if I have data from 2010 on, I have a user input that specifies '2011-10' as the 'earliest date' they want to see data from.
My current SQL looks like this:
select round(sum(amount), 2) as amount,
date_part('month', date) as month
from receipts join items
on receipts.item = items.item
where items.expense = ?
and date_part('year', date)>=2014
and funding = 'General'
group by items.expense, month, items.order
order by items.order desc;
In the second part of the 'where', instead of doing year >= 2014, I want to do something like to_char(date, 'YY-MMMM') >= ? as another parameter and then pass in '2011-10'. However, when I do this:
costsSql = "select round(sum(amount), 2) as amount,
to_char(date, 'YY-MMMM') as year_month
from receipts join items
on receipts.item = items.item
where items.expense = ?
and year_month >= ?
and funding = 'General'
group by items.expense, year_month, items.order
order by items.order desc"
and call that with my two params, I get a postgres error: PG::UndefinedColumn: ERROR: column "year_month" does not exist.
Edit: I converted my YYYY-MM string into a date and passed that in as my param instead and it's working. But I still don't understand why I get the 'column does not exist' error after I created that column in the select clause - can someone explain? Can columns created like that not be used in where clauses?
This error: column "year_month" does not exist happens because year_month is an alias defined the SELECT-list and such aliases can't be refered to in the WHERE clause.
This is based on the fact that the SELECT-list is evaluated after the WHERE clause, see for example: Column alias in where clause? for an explanation from PG developers.
Some databases allow it nonetheless, others don't, and PostgreSQL doesn't. It's one of the many portability hazards between SQL engines.
In the case of the query shown in the question, you don't even need the to_char in the WHERE clause anyway, because as mentioned in the first comment, a direct comparison with a date is simpler and more efficient too.
When a query has a complex expression in the SELECT-list and repeating it in the WHERE clause looks wrong, sometimes it might be refactored to move the expression into a sub-select or a WITH clause at the beginning of the query.

SQL Navigator throws 'ORA-01834: Not a valid month' but query runs in other applications

I have stocked in this error many times but know I have no way to avoid and I have to get rid of it.
Sometimes I do run a query in SQL Navigator 6.1 XPert Edition and it throws:
ORA-01843: Not a valid month
But if I run this same query in same database but in other application(ie Aqua Data Studio) it works fine. It's just in isolated cases.
It may be some config problem?
EDIT: This query has that problem:
select
quantity dias_a_vencer
, estab
, initcap (planejador) planejador
, atributo2 fabrica
, mrp.item montagem
, initcap (descricao) des_montagem
, mrp.nro_docmto num_of
, initcap (mrp.fornecedor) cliente
, mrp.project_number projeto
, initcap (comprador) processista
, trunc (mrp.data_inicio) data_inicio
from etlt_mrp_exceptions mrp
where
mrp.compile_designator = 'ENGI'
and mrp.dt_coleta > sysdate - 50
and estab = '179' -- PARAMETRO ESTAB FILTRO
and atributo2 = '11' -- PARAMETRO FABRICA FILTRO
and nvl (mrp.quantity, 0) > 0
and dt_coleta = '05/12/2011' -- parametro do grafico acima
and initcap (planejador) = 'Maria Cristina Da Cruz Costa' -- parametro do grafico acima
order by quantity
, des_montagem
To make your query fail-safe in all environments, you have to change this line:
and dt_coleta = '05/12/2011'
to
and dt_coleta = to_date('05/12/2011', 'DD/MM/YYYY')
Assuming that you meant December 5th, and not May, 12th.
Btw: what datatype are the columns estab and atributo2. If those are numbers you should remove the single quotes around the parameters. That is another "implicit" data conversion that would e.g. prevent the usage of an index on those columns.
Always specify a date format, never assume it or use default formats. For example:
insert into mytable (mydate) values (to_date('02/28/2011', 'MM/DD/YYYY'));
Unfortunately, even using TO_DATE() does not guarantee success. (But I would strongly recommend that you are always aware of the date format in play.) For instance using SQL*Developer, this works:
alter session set nls_date_format='yyyy-mon-dd hh24:mi:ss.ddd';
select * from nns.nns_logGER WHERE LOG_DATE >= '2014-jul-30 14:47:16.211';
but this fails with "ORA-01834: day of month conflicts with Julian date" error:
alter session set nls_date_format='yyyy-mon-dd hh24:mi:ss.ddd';
select * from nns.nns_logGER WHERE LOG_DATE >= '2014-jul-30 14:47:16.210';
Notice that I changed only the last digit. And using to_date did not help:
select * from nns.nns_logGER WHERE LOG_DATE >=
to_date('2014-jul-30 14:47:16.210','yyyy-mon-dd hh24:mi:ss.ddd');
fails in the same way.
I wish there were better news, but I think this must be some internal problem.

Oracle date function

I am executing the below query,It returns me the blank row.However there are records in the table having upd_time = '12-MAR-08'.I don't understand why it is not returning the date '12-MAR-08'.Please help me out??
SELECT br_data.upd_time FROM BANKREC.br_data
where br_data.upd_time = '12-MAR-08';
It's likely that upd_time isn't exactly 12-MAR-08. The date format is not showing the time component, but it's probably there (DATE data type in Oracle can contain both date and time components).
Try this (it will allow you to see the time components):
alter session set nls_date_format='DD-MON-YY HH24:MI:SS';
SELECT br_data.upd_time FROM BANKREC.br_data
where br_data.upd_time >= to_date('12-MAR-08','DD-MON-YY')
and br_data.upd_time < to_date('13-MAR-08','DD-MON-YY');
Is it the same if you do a
SELECT br_data.upd_time FROM BANKREC.br_data
where trunc(br_data.upd_time) = '12-MAR-08';
It could be that the upd_time is not a date, but a timestamp, so it actually contains for instance '12-MAR-08 05:30' which wouldn't be the same thing. trunc() removes the time part of the timestamp.
Since it doesn't return an error, I assume that it parses the date correctly, but otherwise you could try with to_date('12-MAR-08','DD-MON-YY')
You should use Oracle's function to convert your string properly into a date using
to_date('12-MAR-08', 'DD-MMM-YY')
Then you have to take into account that the Oracle "Date" datatype also contains time information to the nearest second. This means that the date that was constructed in the first step is actually midnight on March 12th. So you have to make sure that the upd_time is truncated to midnight:
trunc(upd_time, 'DAY') = to_date('12-MAR-08', 'DD-MMM-YY')
Your full query becomes
SELECT br_data.upd_time
FROM BANKREC.br_data
WHERE trunc(upd_time, 'DAY') = to_date('12-MAR-08', 'DD-MMM-YY');
There are other ways to skin this cat (you could transfer your updTime column to a proper char field with to_char(upd_time, 'DD-MMM-YY')), but it's usually advisable make the data you are looking for similar to what you can find in the database as that increases your chances of using an index for the lookup.
i don't have access to an oracle db at the moment but i remember using to_char.
try
SELECT br_data.upd_time FROM BANKREC.br_data where to_char(br_data.upd_time, 'DD-MON-YY') = '12-MAR-08';

Resources