Compare date to month-year in Postgres/Ruby - 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.

Related

I tested in my SQL Developer one case about "Subquery in Order By"

I have question about "Subquery in Order by clause". The below request returns the error. Is it means that Subquery in Order by clause must be scalar?
select *
from employees
order by (select * from employees where first_name ='Steven' and last_name='King');
Error:
ORA-00913: too many values
00913. 00000 - "too many values"
Yes, it means that if you use a subquery in ORDER BY it must be scalar.
With select * your subquery returns multiple columns and the DBMS would not know which of these to use for the sorting. And if you selected one column only, you would still have to make sure you only select one row of course. (The difference is that Oracle sees the too-many-columns problem immediately, but detect too many rows only when fetching the data.)
This would be allowed:
select * from employees
order by (select birthdate from employees where employee_id = 12345);
This is a scalar query, because it returns only one value (one column, one row). But of course this still makes as little sense as your original query, because the subquery result is independent from the main query, i.e. it returns the same value for every row in the table and thus no sorting takes effect.
A last remark: A subquery in ORDER BY makes very seldomly sense, because that would mean you order by something you don't display. The exception is when looking up a sortkey. E.g.:
select *
from products p
where type = 'shirt' and color = 'blue' and size in ('S', 'M', 'L', 'XL')
order by (select sortkey from sizes s where s.size = p.size);
It means that valid options for ORDER BY clause can be
expression,
position or
column alias
A subquery is neither of these.

Vertica - Subquery used as an expression returned more than one row

Is it possible to run a query which matches ANY rows in another table column? I'm trying to run this for example:
SELECT *
FROM emails
WHERE address ILIKE '%#' || IN (select * from dictionary.wordlist) || '.%'
However this returns [Vertica]VJDBC ERROR: Subquery used as an expression returned more than one row
Now that's a strange way of formulating it...
If you go back to a basic SQL tutorial, you will understand that a string literal like '%#' , which can be the operand of an ILIKE predicate, cannot be concatenated with an IN () clause - which is a predicate in itself.
I assume that you are looking for all rows in the emails table whose address contains any of the words in dictionary.wordlist between the at-sign and a dot.
I hope (correct me if I'm wrong) that dictionary.wordlist is a table with one column in VARCHAR() or other string format. If that is the case, you can go like this:
WITH
-- out of "dictionary.wordlist", create an in-line-table containing a column
-- with the wildcard operand to be later used in an ILIKE predicate
operands(operand) AS (
SELECT
'%#'||wordlist.word||'.%'
FROM dictionary.wordlist
)
SELECT
emails.*
FROM emails
INNER JOIN operands
ON email.address ILIKE operands.operand
;
There are other ways of doing it, of course, but this is one of them.
I'm not trying to say it will be very fast - an ILIKE predicate as a JOIN condition can't be performant ...
Good luck
Marco the Sane

Nested Subquery Limitations in Oracle

So, I have been doing a fair amount of reading on this in various forums and resource sites but have not yet found found a solution I believe applies to my case. Also, I can't believe how difficult this is proving to be; I would think this kind of query would be fairly common.
Essentially what I am doing here is querying two historical tables (tbl_b and tbl_c), via union, for a specific milestone date - for which there may be multiple results... I then wish to find the most recent of these results, using max. This date is then returned as a column in the main query.
My problem is that, in the 3rd tier subquery, I need to reference an identifier value from the table in the top query (tbl_a).
I know that correlated queries only are able to reference their parent query - so, I am stuck.
Edit 1
The target date I am searching for will most likely, but not necessarily, be unique within the result set. It is a timestamp of the data record. I am looking for the most recent entry in the history that correlates to each column in tbl_a. Creating an SQL Fiddle for this.
See sample below:
select tbl_a.col_a,
tbl_a.col_b,
(
select max(target_date)
from
(
select tbl_b.target_date
from tbl_b
where tbl_b.tbl_a_id = tbl_a.id and
tbl_b.flag = 1 and
tbl_b.milestone_id = tbl_a.milestone_id
union
select tbl_c.target_date
from tbl_c
where tbl_c.tbl_a_id = tbl_a.id and
tbl_c.flag = 1 and
tbl_c.milestone_id = tbl_a.milestone_id
) most_recent_target_date
)
from tbl_a
Convert this query to a join, in this way:
select tbl_a.col_a,
tbl_a.col_b,
max(most_recent_target_date.target_date)
from tbl_a
join (
select tbl_b.target_date, tbl_b.date_id
from tbl_b
where tbl_b.flag = 1
union all
select tbl_c.target_date, tbl_c.date_id
from tbl_c
where tbl_c.flag = 1
) most_recent_target_date
ON tbl_a.date_id = most_recent_target_date.date_id
GROUP BY tbl_a.col_a,
tbl_a.col_b

Searching first condition first and only if not available then second condition

I am writing an SQL query where the query should first search the first value, and only if this value is missing the query should search for the second value.
I have two tables. One of these tables contains the modification date (this is not always filled and can be null) and a creation date which is always filled.
Now what I want is that the query first looks in the table with the modification date and only if it is null looks at the table with the creation date.
Example of the query:
Select *
from all_articles
where to_char(modification_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
-- if this is an empty record then
to_char(creation_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
Can anyone help me with this query?
Almost all the major RDBMS' available have in built functions to handle such a situation.
The Oracle DB has NVL function which works as follows:
NVL(Modified_dt, Create_dt);
The above will return Modified_dt column data by default. However, if that isn't available, it will return Create_dt.
See here for details:
http://www.w3schools.com/sql/sql_isnull.asp

oracle query error: exact fetch return more than requested no of rows

I have two tables seatinfo(siid,seatno,classid,tsid) and booking (bookid,siid,date,status).
I've input parameter bookDate,v_tsId ,v_clsId. I need exactly one row (bookid) to return. This query is not working. I don't no why. How can I fix it?
select bookid
into v_bookid
from booking
where (to_char(booking.bookdate,'dd-mon-yy'))=(to_char(bookDate,'dd-mon-yy'))
and status=0
and rownum <= 1
and siid in(select siid
from seatinfo
where tsid=v_tsId
and classid= v_clsId);
I also tried this:
select bookid
into v_bookid
from booking,
seatinfo
where booking.siid=seatinfo.siid
and (to_char(booking.bookdate,'dd-mon-yy'))=(to_char(bookDate,'dd-mon-yy'))
and booking.status=0
and rownum <= 1
and seatinfo.tsid=v_tsId
and seatinfo.classid= v_clsId;
Are you saying that you get an "ORA-01422: exact fetch returns more than requested number of rows" when you run both of those queries? That seems highly unlikely since you're including the predicate rownum <= 1. Can you cut and paste from a SQL*Plus session that runs just this query in a PL/SQL block and generates the error?
If you are not complaining about the error you mention in the title, and the problem is just that you're not getting the data you expect, the likely problem is that you apparently have a bookDate parameter that has the same name as a column in your table. That is not going to work. When you say
(to_char(booking.bookdate,'dd-mon-yy'))=(to_char(bookDate,'dd-mon-yy'))
you presumably mean to compare the bookDate column in the booking table against the bookDate parameter. But since column names have precedence over local variables, the left-hand side of your expression is also looking at the bookDate column in the booking table. So you're comparing a column to itself. It would make much more sense to change the name of the parameter (to, say, p_bookDate) and then write
booking.bookDate = p_bookDate
or, if you want to do the comparison ignoring the time component of the dates
trunc( booking.bookDate ) = trunc( p_bookDate )

Resources