Case When function with multiple conditions in Cognos Analytics 11.1.x - case

I am using Cognos embedded in Watson Studio and attempted creating the following calculation with case statement (both month_3 and avg2m are measures).
Cognos reports XQE-V5-0017 V5 syntax error found for data item 'calculation-new' of query 'validateQuery', invalid token "<" found after "case (( allin_shaped_csv.month_3 - avg2m ) / avg2m ) when ".
May you help fixing the syntax error?
case (( allin_shaped_csv.month_3 - avg2m ) / avg2m )
when <-0.50 then -100;
when <-0.20 then -50;
when <0 then -20;
when 0 then 0;
when >0 then 20;
when >0.50 then 50;
when >0.99 then 100;
end case;

it looks it works this way :
case
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )<-0.50 then -100
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )<-0.20 then -50
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )<0 then -20
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )=0 then 0
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )>0 then 20
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )>0.50 then 50
when (( allin_shaped_csv.month_3 - avg2m ) / avg2m )>0.99 then 100
else 0
end

Related

Can I create a date variable in R for dbGetQuery?

This is my code:
cohort_query <- dbGetQuery(con,'select u.unique_id customer_id,
o.user_id user_hash,
"min"(o.deliveryconfirmeddate) first_order,
(
CASE
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'1\' YEAR)
) THEN 0
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'2\' YEAR)
) THEN 1
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'3\' YEAR)
) THEN 2
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'4\' YEAR)
) THEN 3
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'5\' YEAR)
) THEN 4
ELSE 5
END
) "cohort_year",
min(o.id) as first_order_id
FROM (
"bj-analytics"."mysql_bj_orders" o
INNER JOIN "bj-analytics"."mysql_bj_users" u ON (u.user_id = o.user_id)
)
WHERE (
(
(o.connectedorder = false)
AND (o.status <> \'cancelled\')
)
AND (o.status <> \'Cancelled\')
)
GROUP BY 1,
2
HAVING (
"min"(o.deliveryconfirmeddate) >= (date(\'2021-11-01\') - INTERVAL \'6\' YEAR)
)
and "min"(o.deliveryconfirmeddate) < date(\'2021-11-01\')')
This all runs fine. Basically, I want to make the 2021-11-01 date a variable, so that I only have to enter it once.
Someone recommended glue_sql but I couldn't make it work. I think it's an issue with the double quotation marks and the single marks. I tried to change these to all "" or to all '' but then the query just doesn't run!
I also found that I have to include \'s - again without them doesn't seem to run.
I'm new to R so not too sure how to get around this!
Basically can anyone please help with turning that date into a variable?
Any help is much appreciated - thanks!
Never use paste or sprintf to put "data" into a query, for many reasons (query optimization/caching and accidental sql-injection being the top two), instead use parameter-binding:
DBI::dbGetQuery(con, "
select (case
when (5 < ?) then 5
when (3 < ?) then 3
when (1 < ?) then 1
else 0 end) as quux",
params = replicate(n=3, expr=3, simplify = FALSE))
# quux
# 1 1
DBI::dbGetQuery(con, "
select (case
when (5 < ?) then 5
when (3 < ?) then 3
when (1 < ?) then 1
else 0 end) as quux",
params = replicate(n=3, expr=10, simplify = FALSE))
# quux
# 1 5
The replicate(3,10,F) is just a programmatic way to do list(10,10,10), they are equivalent here. My use of static comparisons (5 < ?) is solely for placeholders, your min(.) should work fine.
(FYI, one more benefit of parameter-binding: no need to deal with additional quotes in the query.)
See https://db.rstudio.com/best-practices/run-queries-safely/#parameterized-queries for more discussions on safely using data in the query.
I think that would make your query this:
cohort_query <- dbGetQuery(con, 'select u.unique_id customer_id,
o.user_id user_hash,
"min"(o.deliveryconfirmeddate) first_order,
(
CASE
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'1\' YEAR)
) THEN 0
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'2\' YEAR)
) THEN 1
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'3\' YEAR)
) THEN 2
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'4\' YEAR)
) THEN 3
WHEN (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'5\' YEAR)
) THEN 4
ELSE 5
END
) "cohort_year",
min(o.id) as first_order_id
FROM (
"bj-analytics"."mysql_bj_orders" o
INNER JOIN "bj-analytics"."mysql_bj_users" u ON (u.user_id = o.user_id)
)
WHERE (
(
(o.connectedorder = false)
AND (o.status <> \'cancelled\')
)
AND (o.status <> \'Cancelled\')
)
GROUP BY 1,
2
HAVING (
"min"(o.deliveryconfirmeddate) >= (date(?) - INTERVAL \'6\' YEAR)
)
and "min"(o.deliveryconfirmeddate) < date(?)'),
params = replicate(n=7, expr='2021-11-01', simplify=FALSE)

How do I print a triangle of stars using PL/SQL

Is it practically possible to create a triangle of stars like this as below in PL/SQL. I know that this could be done easily in any other programming language like C,C++,Java but want to know whether it is really possible with just SQL or PL/SQL. This is for my homework and I should use conditional clauses (IF THEN ELSE), loops(FOR, WHILE).
*
***
*****
*******
*********
***********
*************
and
*****
***
*
Try this.
The first loop will print the stars in triangle and the second loop will reverse it.
In PL/SQL:
BEGIN
FOR i IN 1 .. :p
LOOP
DBMS_OUTPUT.put_line (LPAD (LPAD ('*', i, '*'), :p + 1, ' '));
END LOOP;
FOR i IN 1 .. :p
LOOP
DBMS_OUTPUT.put_line (LPAD (LPAD ('*', :p-i, '*'), :p + 1, ' '));
END LOOP;
END;
In SQL:
SELECT LPAD (LPAD ('*', level, '*'), :p + 1, ' ') a
FROM DUAL
CONNECT BY LEVEL <= :p;
This can be done purely in sql (in Oracle), like so:
SELECT RPAD(' ', :p_num_triangle_rows - LEVEL) || RPAD('*', LEVEL * 2 -1, '*') || RPAD(' ', :p_num_triangle_rows - LEVEL) triangle
FROM dual
CONNECT BY LEVEL <= :p_num_triangle_rows
ORDER BY CASE WHEN :p_ascending_or_descending = 'a' THEN LEVEL END ASC,
CASE WHEN :p_ascending_or_descending = 'd' THEN LEVEL END DESC;
p_num_triangle_rows := 20, p_ascending_or_desc := 'a':
TRIANGLE
--------------------------------------------------------------------------------
*
***
*****
*******
*********
***********
*************
***************
*****************
*******************
*********************
***********************
*************************
***************************
*****************************
*******************************
*********************************
***********************************
*************************************
***************************************
p_num_triangle_rows := 3, p_ascending_or_desc := 'd':
TRIANGLE
--------------------------------------------------------------------------------
*****
***
*
ETA: Here is a PL/SQL version that will do what you're after:
DECLARE
PROCEDURE produce_triangle_rows (p_num_triangle_rows IN NUMBER,
p_ascending_or_descending IN VARCHAR2 DEFAULT 'a')
IS
BEGIN
dbms_output.put_line('p_num_triangle_rows = '|| p_num_triangle_rows ||', p_ascending_or_descending = ' || p_ascending_or_descending);
FOR i IN 1..p_num_triangle_rows
LOOP
CASE WHEN p_ascending_or_descending = 'a' THEN
dbms_output.put_line(RPAD(' ', p_num_triangle_rows - i) || RPAD('*', i * 2 - 1, '*') || RPAD(' ', p_num_triangle_rows - i));
WHEN p_ascending_or_descending = 'd' THEN
dbms_output.put_line(RPAD(' ', i - 1) || RPAD('*', 2 * (p_num_triangle_rows - i) + 1, '*') || RPAD(' ', i - 1));
END CASE;
END LOOP;
END produce_triangle_rows;
BEGIN
produce_triangle_rows(p_num_triangle_rows => 5,
p_ascending_or_descending => 'a');
produce_triangle_rows(p_num_triangle_rows => 3,
p_ascending_or_descending => 'd');
END;
/
p_num_triangle_rows = 5, p_ascending_or_descending = a
*
***
*****
*******
*********
p_num_triangle_rows = 3, p_ascending_or_descending = d
*****
***
*
Note that I've wrapped the procedure in an anonymous block purely so I could call it with different parameters. You would just create the produce_triangle_rows procedure on its own and then call it appropriately.
Try this
declare #count int,#num int,#num1 int, #space int, #str varchar(50)
set #count = 3 set #num = 1
while(#num<=#count)
begin
set #num1 = 0 set #space = #count-#num
while (#num1<#num)
begin
if #str is null
set #str = '* '
else
set #str = #str+'* ' set #num1 = #num1+1
end
print (space(#space)+#str)
set #num = #num+1 set #str = null
end
Or
Declare #x varchar(20)=0,#y varchar(20)=5
while(#y>0)
begin
print space(#y)+replicate('*',#x)+replicate('*',#x+1)
set #y=#y-1
set #x=#x+1
end
I found the problem fun, so I solved it in PostgreSQL. Unfortunately repeating characters seems not really be a standardized feature. Here are the syntax to use for the most common RDBMSes:
PostgreSQL : repeat('*', n)
Oracle : rpad('', n, '*')
MS SQL Server : replicate('*', n)
MySQL : repeat('*', n)
First create some table with numbers up to whatever is the biggest triangle you can imagine:
create table n10 (n) as
select 0 union
select 1 union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9;
create table n1000 (n) as
select 100 * a1.n + 10 * a2.n + a3.n
from n10 a1 cross join n10 a2 cross join n10 a3;
Then to make a triangle of size 5 (replace 5 by any number between 0 and 1000 you want):
with s (s) as (select 5)
select
repeat(' ', s - n - 1) ||
repeat('*', 2 * n + 1) ||
repeat(' ', s - n - 1)
from n1000 cross join s
where n < s
order by n;
with s (s) as (select 5)
select
repeat(' ', s - n - 1) ||
repeat('*', 2 * n + 1) ||
repeat(' ', s - n - 1)
from n1000 cross join s
where n < s
order by n desc;

PL/SQL Using CASE in WHERE clause

Good day Stackoverflow!
I have a query that is giving me an error: "Missing Right Parenthesis", at least, so says SQL Developer.
My query has a CASE statement within the WHERE clause that takes a parameter, and then executing a condition based on the value entered.
I've read that when using a CASE statement within a WHERE clause you have to surround the statement with parenthesis and assign it to a numeric value, e.g. "1", however doing so does not accomplish my goal.
My goal is to execute a condition in the CASE statement if that condition is met.
Would you mind taking a look and giving me some input please?
Thanks!
SELECT ...
FROM ....
WHERE 1 = 1
AND (
CASE :P_REPORT_PERIOD
WHEN 'SPRING'
THEN ((fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))-1) AND period >=10) OR (fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period < 4))
WHEN 'FALL'
THEN ((fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period >=4) OR (fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period < 10))
END
) = 1
Problem Solved, THANKS to all those that attempted finding a solution.
Solution: Rather than using a CASE statement, I just created a stored procedure, replaced the CASE with IF and built a VSQL string from my query.
Example:
VSQL := 'SELECT.....'
IF (v_rpt_pd = 'SPRING') THEN
VSQL := VSQL || '( ( AND EXTRACT(YEAR FROM (SYSDATE))-1 = fiscal_year and period >=10) or ';
VSQL := VSQL || ' ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period <=3) )';
ELSE
VSQL := VSQL || '( ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period >=4) or ';
VSQL := VSQL || ' ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period <=9) )';
END IF;
VSQL := VSQL ||' GROUP BY fiscal_year, period
and so on, if you want the entire solution, DM me and I'll send you the code.
Cheers!
As per Tom the CASE syntax in WHERE CLAUSE is -
--Syntax
select * from <table name>
where 1=1
and
(case
when <BOOLEAN_EXPRESSION>
then <SCALAR_RETURN_VALUE>
...
ELSE <SCALAR_RETURN_VALUE>
end) = <SCALAR_VALUE> ;
Example:
--Query
WITH SAMPLE_DATA AS
(select 100 COL1,999 COL2 from DUAL UNION ALL
select 200 COL1,888 COL2 from DUAL
)
SELECT * FROM SAMPLE_DATA
WHERE 1=1
AND (
CASE COL2
WHEN 999 THEN 1
ELSE 0
END
) = 1 ;
-- Output:
100 999

Group By breaks Having clause

I am querying the DB to get all venues within a certain X Radius of a given latitude & longitude.
Everything works good until I insert a count & group by, causing the Having clause to no longer filter out records that are
SELECT
locations.id AS 'locations_id_c',
locations.city AS 'locations_city_c',
locations.province AS 'locations_state_c',
locations.latitude AS 'latitude_c',
locations.longitude AS 'longitude_c',
venues.id AS 'venues_id_c',
venues.name AS 'venues_name_c',
venues.venue_rank,
venues.median_fb_likes,
count(gigs.show_time),
count(band_profiles.name) AS 'Related Band',
#san fran lat & long = 37.7833° N, 122.4167° W
# ( 3959 * acos( cos( radians(37.7833) ) * cos( radians( locations.latitude) ) * cos( radians( locations.longitude ) - radians(-122.4167) ) + sin( radians(37.7833) ) * sin(radians(locations.latitude)) ) ) AS 'distance'
3956 * 2 * ASIN(SQRT( POWER(SIN((37.7833 - abs(locations.latitude)) * pi()/180/2),2) +COS(37.7833 * pi()/180 )*COS(abs(locations.latitude)*pi()/180) *POWER(SIN((locations.longitude + 122.4167)*pi()/180/2),2))) as 'distance'
FROM locations
INNER JOIN venues
ON locations.locatable_id = venues.id
AND venues.venue_rank >= 0
LEFT JOIN gigs
ON gigs.venue_id = venues.id
and gigs.show_time > NOW()
LEFT JOIN band_profiles
ON gigs.account_id = band_profiles.account_id
and band_profiles.name IS NOT NULL
WHERE
locations.locatable_type = 'Venue'
group by venues.id
HAVING distance < 50
ORDER BY venues.venue_rank DESC, distance DESC;
got it working
Seems the group by must happen on the main table, and not one of the joins.
Does anyone have an explanation why this is so?

Symfony2, Doctrine search and order by distance

I have a database of places which I store the latitude and longitude for. I want to query the database to find all places within a radius ($requestedDistance) of a specific latitude ($latitude) and longitude ($longitude).
The below query works and returns only those places within this radius, however how would I order them by distance so the closest is first? In the past, using raw SQL I have done the calculation within the SELECT statement and set it as 'distance' and then used HAVING distance < $requestedDistance ORDER BY distance, however I'm not sure how to add the calculation to the SELECT query using Doctrine Repository.
$d = $this->getDoctrine()->getRepository('XXXWebsiteBundle:Locations')->createQueryBuilder('l');
$d
->add('where','l.enabled = 1')
->andWhere('( 3959 * acos( cos( radians('.$latitude.') )
* cos( radians( l.latitude ) )
* cos( radians( l.longitude )
- radians('.$longitude.') )
+ sin( radians('.$latitude.') )
* sin( radians( l.latitude ) ) ) ) < '.$requestedDistance);
$result= $d->getQuery();
UPDATE
I've tried the following query thanks to #Lazy Ants:
$d = $this->getDoctrine()->getRepository('XXXWebsiteBundle:Locations')->createQueryBuilder('l');
$d
->select('l')
->addSelect(
'( 3959 * acos(cos(radians(' . $latitude . '))' .
'* cos( radians( l.latitude ) )' .
'* cos( radians( l.longitude )' .
'- radians(' . $longitude . ') )' .
'+ sin( radians(' . $latitude . ') )' .
'* sin( radians( l.latitude ) ) ) ) as distance'
)
->andWhere('l.enabled = :enabled')
->setParameter('enabled', 1)
->andWhere('distance < :distance')
->setParameter('distance', $requestedDistance)
->orderBy('distance', 'ASC');
However, it returns the following error:
`An exception occurred while executing 'SELECT COUNT(*) AS dctrn_count FROM
(SELECT DISTINCT il0 FROM (SELECT l0_.id AS il0, l0_.name AS name2, l0_.address1
AS address14, l0_.address2 AS address25, l0_.postcode AS postcode6, l0_.town AS
town7, l0_.county AS county8, l0_.enabled AS enabled11, l0_.date_created AS
date_created12, l0_.date_modified AS date_modified13, l0_.latitude AS
latitude19, l0_.longitude AS longitude20, 3959 * ACOS(COS(RADIANS(53.51331889999999))
* COS(RADIANS(l0_.latitude)) * COS(RADIANS(l0_.longitude) -
RADIANS(-2.935331099999985)) + SIN(RADIANS(53.51331889999999)) *
SIN(RADIANS(l0_.latitude))) AS sclr21 FROM locations l0_ WHERE l0_.enabled = ?
AND sclr21 < ? ORDER BY sclr21 ASC) dctrn_result) dctrn_table' with params
{"1":1,"2":30}:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sclr21' in 'where clause'`
$d = $this->getDoctrine()->getRepository('XXXWebsiteBundle:Locations')->createQueryBuilder('l');
$d
->select('l')
->addSelect(
'( 3959 * acos(cos(radians(' . $latitude . '))' .
'* cos( radians( l.latitude ) )' .
'* cos( radians( l.longitude )' .
'- radians(' . $longitude . ') )' .
'+ sin( radians(' . $latitude . ') )' .
'* sin( radians( l.latitude ) ) ) ) as distance'
)
->andWhere('l.enabled = :enabled')
->setParameter('enabled', 1)
->having('distance < :distance')
->setParameter('distance', $requestedDistance)
->orderBy('distance', 'ASC');
You are using andWhere() without a where().
Replace the andWhere() with a where().
Please check also in your Entity definition if some property is mapped on a column called "sclr21", so you can discovery also on which property do you have the issue.

Resources