postgresql do not return values on specific numeric criterias - sql

I have a postgresql database which look like the following :
+---------------+---------------- ------+------------ ---+
| id (bigint) | name (varying(255)) | price (real) |
+---------------+---------------- ------+------------ ---+
| 1 | name 1 | 0.33 |
+---------------+---------------- ------+------------ ---+
| 1 | name 2 | 1.33 |
+---------------+---------------- ------+------------ ---+
| 1 | name 3 | 1 |
+---------------+---------------- ------+------------ ---+
And then the results of my queries :
SELECT * FROM my_table WHERE price = 1 -- OK (one row returned)
SELECT * FROM my_table WHERE price = 1.0 -- OK (one row returned)
SELECT * FROM my_table WHERE price = 1.33 -- FAIL (no row returned)
SELECT * FROM my_table WHERE price = 0.33 -- FAIL (no row returned)
When the value can't be cast to an non-floating value no lines are returned by postgresql.
I can't figure out why. Have you the same problem ? How can I fix this ?

One solution I see is to use explicit cast to real datatype:
SELECT * FROM my_table WHERE price = 0.33::real;
id | name | price
----+--------+-------
1 | name 1 | 0.33
SELECT * FROM my_table WHERE price = 1.33::real;
id | name | price
----+--------+-------
1 | name 2 | 1.33
According to documentation:
A numeric constant that contains neither a decimal point nor an
exponent is initially presumed to be type integer if its value fits in
type integer (32 bits); otherwise it is presumed to be type bigint if
its value fits in type bigint (64 bits); otherwise it is taken to be
type numeric. Constants that contain decimal points and/or exponents
are always initially presumed to be type numeric.
Note that:
SELECT 1.33::numeric = 1.33::real;
?column?
----------
f
(1 row)

Related

PostgreSQL Compare value from row to value in next row (different column)

I have a table of encounters called user_dates that is ordered by 'user' and 'start' like below. I want to create a column indicating whether an encounter was followed up by another encounter within 30 days. So basically I want to go row by row checking if "encounter_stop" is within 30 days of "encounter_start" in the following row (as long as the following row is the same user).
user | encounter_start | encounter_stop
A | 4-16-1989 | 4-20-1989
A | 4-24-1989 | 5-1-1989
A | 6-14-1993 | 6-27-1993
A | 12-24-1999 | 1-2-2000
A | 1-19-2000 | 1-24-2000
B | 2-2-2000 | 2-7-2000
B | 5-27-2001 | 6-4-2001
I want a table like this:
user | encounter_start | encounter_stop | subsequent_encounter_within_30_days
A | 4-16-1989 | 4-20-1989 | 1
A | 4-24-1989 | 5-1-1989 | 0
A | 6-14-1993 | 6-27-1993 | 0
A | 12-24-1999 | 1-2-2000 | 1
A | 1-19-2000 | 1-24-2000 | 0
B | 2-2-2000 | 2-7-2000 | 1
B | 5-27-2001 | 6-4-2001 | 0
You can select..., exists <select ... criteria>, that would return a boolean (always true or false) but if really want 1 or 0 just cast the result to integer: true=>1 and false=>0. See Demo
select ts1.user_id
, ts1.encounter_start
, ts1. encounter_stop
, (exists ( select null
from test_set ts2
where ts1.user_id = ts2.user_id
and ts2.encounter_start
between ts1.encounter_stop
and (ts1.encounter_stop + interval '30 days')::date
)::integer
) subsequent_encounter_within_30_days
from test_set ts1
order by user_id, encounter_start;
Difference: The above (and demo) disagree with your expected result:
B | 2-2-2000 | 2-7-2000| 1
subsequent_encounter (last column) should be 0. This entry starts and ends in Feb 2000, the other B entry starts In May 2001. Please explain how these are within 30 days (other than just a simple typo that is).
Caution: Do not use user as a column name. It is both a Postgres and SQL Standard reserved word. You can sometimes get away with it or double quote it. If you double quote it you MUST always do so. The big problem being it has a predefined meaning (run select user;) and if you forget to double quote is does not necessary produce an error or exception; it is much worse - wrong results.

Retrieve field offset via recursive query in db2

Assume that I've got key-value table of field_name-field_len pair.
As follows:
-----------------------------------
field_name | field_len |
-----------------------------------
FIELD_A | 10 |
-----------------------------------
FIELD_B | 20 |
-----------------------------------
...
-----------------------------------
FIELD_X | 2 |
-----------------------------------
FIELD_Y | 100 |
-----------------------------------
Then I need an offset of each field to be in third column.
Like this:
-----------------------------------------------------
field_name | field_len | offset |
-----------------------------------------------------
FIELD_A | 10 | 0 |
-----------------------------------------------------
FIELD_B | 20 | 10 |
-----------------------------------------------------
...
-----------------------------------------------------
FIELD_X | 2 | 250 |
-----------------------------------------------------
FIELD_Y | 100 | 252 |
-----------------------------------------------------
So I've wrote this script based on some manuals (1,2):
with offsets (column_name, length, offset) as
((select column_name, length, CAST(0 AS SMALLINT)
from myschema.sizes a
start with rrn(a) = 1)
union all
(select b.column_name, b.length, offset + o.length
from offsets o, myschema.sizes b
where rrn(b) between 2 and 100))
select * from offsets;
However, it keeps getting into infinite loop.
Also this version gives same result:
with offsets (column_name, length, offset) as
((select column_name, length, CAST(0 AS SMALLINT)
from myschema.sizes a
fetch first row only)
union all
(select b.column_name, b.length, offset + o.length
from offsets o join myschema.sizes b on b.column_name = o.column_name
where o.column_name <>'LAST_FIELD'))
select * from offsets;
I guess, that messed somewhere with exit condition, but can not figure exact place to fix it.
Would be great to avoid any table specific metadata like row count too.
You don't need a recursive CTE for this. Just a cumulative sum. Something like this:
select s.*,
(sum(field_len) over (order by rrn(s)) - field_len) as offset
from myschema.sizes s;
I'm not sure how the ordering is defined. It seems to be based on a function rrn().

How to convert string to number based on units

I am trying to change the following strings into their respective numerical values, by identifying the units (millions or billions) and then multiplying accordingly. I believe I am having issues with the variable types but can't seem to find a solution. Any tips?
1.44B to 1,440,000,000
1.564M to 1,564,000
UPDATE [_ParsedXML_Key_Stats]
SET [Value] = CASE
WHEN right(rtrim([_ParsedXML_Key_Stats].[Value]),1) = 'B' And [_ParsedXML_Key_Stats].[NodeName] = 'EBITDA'
THEN substring(rtrim([_ParsedXML_Key_Stats].[Value]),1,len([_ParsedXML_Key_Stats].[Value])-1) * 1000000000
WHEN right(rtrim([_ParsedXML_Key_Stats].[Value]),1) = 'M' And [_ParsedXML_Key_Stats].[NodeName] = 'EBITDA'
THEN substring(rtrim([_ParsedXML_Key_Stats].[Value]),1,len([_ParsedXML_Key_Stats].[Value])-1) * 1000000
ELSE 0
END
With your original query I got a conversion error as the multiplication was treating the decimal value as an int, I guess you might have experienced the same problem.
One remedy that fixed it was to turn the factor into a decimal by adding .0 to it.
If you want to get the number formatted with commas you can use format function like so: FORMAT(CAST(value AS DECIMAL), 'N0') (be sure to specify appropriate length and precision for the decimal type).
Sample test data and output from SQL Fiddle below:
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE [_ParsedXML_Key_Stats] (value VARCHAR(50), NodeName VARCHAR(50));
INSERT [_ParsedXML_Key_Stats] VALUES
('111', 'SOMETHING ELSE'),
('999', 'EBITDA'),
('47.13B', 'EBITDA'),
('1.44B', 'EBITDA'),
('1.564M', 'EBITDA');
WITH cte AS
(
SELECT
Value,
CAST(LEFT([Value],LEN([Value])-1) AS DECIMAL(28,6)) AS newValue,
RIGHT(RTRIM([Value]),1) AS c
FROM [_ParsedXML_Key_Stats]
WHERE [NodeName] = 'EBITDA'
AND RIGHT(RTRIM([Value]),1) IN ('B','M')
)
UPDATE cte
SET [Value] =
CASE
WHEN c = 'B' THEN newValue * 1000000000.0
WHEN c = 'M' THEN newValue * 1000000.0
END;
Query 1:
SELECT *, FORMAT(CAST(Value AS DECIMAL(18,0)),'N0') AS formattedValue
FROM _ParsedXML_Key_Stats
Results:
| value | NodeName | formattedValue |
|--------------------|----------------|----------------|
| 111 | SOMETHING ELSE | 111 |
| 999 | EBITDA | 999 |
| 47130000000.000000 | EBITDA | 47,130,000,000 |
| 1440000000.000000 | EBITDA | 1,440,000,000 |
| 1564000.000000 | EBITDA | 1,564,000 |

PostgreSQL IS NULL and length

I am trying to get all records from a table where a specific column is NULL. But I am not getting any records. On the other hand, there are many records where the length(field) is indeed 0.
select count(*) from article a where length(for_interest) =0;
count
-------
9
(1 row)
select count(*) from article a where for_interest is NULL ;
count
-------
0
(1 row)
Something about NULLs I didn't get right? More info
select count(*) from article AS a where for_interest is NOT NULL ;
count
-------
15
(1 row)
select count(*) from article ;
count
-------
15
(1 row)
PostgreSQL version is 9.3.2.
Adding sample data, table description etc (new sample table created with just 2 records for this)
test=# \d sample_article
Table "public.sample_article"
Column | Type | Modifiers
--------------+------------------------+-----------
id | integer |
title | character varying(150) |
for_interest | character varying(512) |
test=# select * from sample_article where length(for_interest)=0;
id | title | for_interest
----+-----------------------------------------------------------------+--------------
8 | What is the Logic Behind the Most Popular Interview Questions? |
(1 row)
test=# select * from sample_article where for_interest IS NOT NULL;
id | title | for_interest
----+-----------------------------------------------------------------+--------------
7 | Auto Expo 2014: Bike Gallery | Wheels
8 | What is the Logic Behind the Most Popular Interview Questions? |
(2 rows)
test=# select * from sample_article where for_interest IS NULL;
id | title | for_interest
----+-------+--------------
(0 rows)
Character types can hold the empty string '', which is not a NULL value.
The length of an empty string is 0. The length of a NULL value is NULL.
Most functions return NULL for NULL input.
SELECT length(''); --> 0
SELECT length(NULL); --> NULL
SELECT NULL IS NULL; --> TRUE
SELECT '' IS NULL; --> FALSE

Multiplying values from two Access tables

I have two tables as below in Accecss 2007.
Town Table
----------
TownName | FlatCount | DetachedCount | SemiCount
A | 5 | 3 | 4
B | 2 | 6 | 3
Cost Table
----------
Prop | PCost
Flat | 10
Detached | 20
Semi | 30
I would like to get an output like below by multiplying the Count from the Town table with the corresponding PCost in the Cost table. FlatCost = Town.FlatCount * Cost.PCost for a flat.
Results
-------
Town | FlatCount | FlatCost | DetachedCount | DetachedCost | .....
A | 5 | 50 | 3 | 60 |
B | 2 | 20 | 6 | 120 |
I have tried to do this by using IIF, but not sure how to get PCost for each property type within the IIF clause.
Thanks
Looks like you are mixing data and meta data e.g. the data value Flat in table Cost becomes metadata value (column name) FlatCount in table Town. This is not a good idea and is probably why you are having difficulties writing what should be a simply query.
Restructure your Town table so that it has columns TownName, Prop and PCount. And remember that most bad SQL DML is caused by bad SQL DDL ;)
You could use a subquery to retrieve the cost of an item:
select TownName
, FlatCount
, FlatCount * (select PCost from Cost where Prop = 'Flat') as FlatCost
, DetachedCount
, DetachedCount * (select PCost from Cost where Prop = 'Detached')
as DetachedCost
, ...
from Town
You have to cross join the tables. Then, for good values, put PCost in the multiplication, else, put 0.
You can then do a SUM using a Group by :
SELECT t.Town,
t.FlatCount,
SUM(t.FlatCount * IIF(c.Prop = 'Flat', c.PCost, 0)) AS FlatCost,
t.DetachedCount,
SUM(t.DetachedCount * IIF(c.Prop = 'Detached', c.PCost, 0)) AS DetachedCost,
FROM Town t, Cost c
GROUP BY t.Town, t.FlatCount, t.DetachedCount