Oracle - Map column data to a value - sql

Let me first point out that my question is going to be very very close to this question: map-column-data-to-a-value-oracle
Please quickly read that one first.
Now in my case I need the exact same thing but not as the primary query. Instead I need the information as one part of my query.
I have this table:
someId | someValue | dataType
1 | 500 | 1
2 | someValue | 2
And I know that dataType "1" means "Integer". I also know the meaning of the other values in the dataType column.
So I want to select all entries in the table but have their dataTypes as their human readable values instead of their numbers:
Results:
1, 500, Integer
2, someString, String
Trying to apply the solution of the question I linked, I created a subquery like
SELECT
someId,
someValue,
(
SELECT CASE
WHEN dataType = 1 THEN 'INTEGER'
WHEN dataType = 2 THEN 'FLOAT'
WHEN dataType = 3 THEN 'TEXT'
ELSE 'DATE'
END
myTable
) as myDataType
I will get a subquery that returns more than 1 result and Oracle will complain.
Since I access the DB through SQL directly, I need a "pure SQL" solution. Otherwise I could just parse the value through a mapping, in say PHP. But that's not possible here. I am shooting some queries at a DB to try and gather information about the data and structure, which we don't know about. So only SQL is available.

Get rid of the subquery:
SELECT someId,
someValue,
CASE
WHEN dataType = 1 THEN 'INTEGER'
WHEN dataType = 2 THEN 'FLOAT'
WHEN dataType = 3 THEN 'TEXT'
ELSE 'DATE'
END as Datatype
from myTable

Related

Store int, float and boolean in same database column

Is there a sane way of storing int, float and boolean values in the same column in Postgres?
If have something like that:
rid
time
value
2d9c5bdc-dfc5-4ce5-888f-59d06b5065d0
2021-01-01 00:00:10.000000 +00:00
true
039264ad-af42-43a0-806b-294c878827fe
2020-01-03 10:00:00.000000 +00:00
2
b3b1f808-d3c3-4b6a-8fe6-c9f5af61d517
2021-01-01 00:00:10.000000 +00:00
43.2
Currently I'm using jsonb to store it, the problem however now is, that I can't filter in the table with for instance the greater operator.
The query
SELECT *
FROM points
WHERE value > 0;
gives back the error:
ERROR: operator does not exist: jsonb > integer: No operator matches the given name and argument types. You might need to add explicit type casts.
For me it's okay to handle boolean as 1 or 0 in case of true or false. Is there any possibility to achieve that with jsonb or is there maybe another super type which lets me use a column that is able to use all three types?
Performance is not so much of a concern here, as I'm going to have very few records inside of that table, max 5k I guess.
If you were just storing integers and floats, normally you'd use a float or numeric column.
But there's that pesky true.
You could cast the JSON...
select *
from test
where value::float > 1;
...but there's that pesky true.
You have to convert the boolean to a number to make it work.
select *
from test
where
(case when value = 'true' then 1.0 when value = 'false' then 0.0 else value::float end) >= 1;
Or ignore it.
This having to work around the type system suggests that value is actually two or even three different fields crammed into one. Consider separating them into multiple columns.
You should skip the rows where value is not number and cast the value to numeric, e.g.:
with points(id, value) as (
values
(1, 'true'::jsonb),
(2, '2'),
(3, '43.2')
)
select *
from points
where jsonb_typeof(value) = 'number'
and value::text::numeric > 0;
id | value
----+-------
2 | 2
3 | 43.2
(2 rows)
I actually found out, regardless of the jsonb fields value, that you can compare it to other jsonb in postgres. That means, I can for instance do the following:
SELECT *
FROM points
WHERE val > '5'
This correctly gives me back only the third row. It just ignores the bool value. To filter for a certain bool I can achieve that with the following query:
SELECT *
FROM points
WHERE val = 'true'
This is good enough for me. I even could hold timestamps in the json column and compare them using this methodology.
Another way of solving the problem after all your comments seem to be to make the column a numeric. This would work as well, but requires more client side conversion, as I would have to have a second type column, remembering what the actual type is. This type should than be used on the client side to convert the value back into its og value. For integers its trivial, for booleans like #schwern suggested, one can use 1 and 0, for dates, one could use the unix timestamp representation.
When I now want to search for a certain value, the type has to be contained in the where clause as well.

How to do a count of fields in SQL with wrong datatype

I am trying to import legacy data from another system into our system. The problem I am having is that the legacy data is dirty- very dirty! We have a field which should be an integer, but sometimes is a varchar, and the field is defined as a varchar...
In SQL Server, how can I do a select to show those records where the data is varchar instead if int?
Thanks
If you want to find rows1 where a column contains any non-digit characters or is longer than 9 characters (either condition means that we cannot assume it would fit in an int, use something like:
SELECT * FROM Table WHERE LEN(ColumnName) > 9 or ColumnName LIKE '%[^0-9]%'
Not that there's a negative in the LIKE condition - we're trying to find a string that contains at least one non-digit character.
A more modern approach would be to use TRY_CAST or TRY_CONVERT. But note that a failed conversion returns NULL and NULL is perfectly valid for an int!
SELECT * FROM Table WHERE ColumnName is not null and try_cast(ColumnName as int) is null
ISNUMERIC isn't appropriate. It answers a question nobody has ever wanted to ask (IMO) - "Can this string be converted to any of the numeric data types (I don't care which ones and I don't want you to tell me which ones either)?"
ISNUMERIC('$,,,,,,,.') is 1. That should tell you all you need to know about this function.
1If you just want a count, as per the title of the question, then substitute COUNT(*) for *.
In SQL Server, how can I do a select to show those records where the data is varchar instead of int?
I would do it like
CREATE TABLE T
(
Data VARCHAR(50)
);
INSERT INTO T VALUES
('102'),
(NULL),
('11Blah'),
('5'),
('Unknown'),
('1ThinkPad123'),
('-11');
SELECT Data -- Per the title COUNT(Data)
FROM
(
SELECT Data,
cast('' as xml).value('sql:column("Data") cast as xs:int ?','int') Result
FROM T --You can add WHERE Data IS NOT NULL to exclude NULLs
) TT
WHERE Result IS NULL;
Returns:
+----+--------------+
| | Data |
+----+--------------+
| 1 | NULL |
| 2 | 11Blah |
| 3 | Unknown |
| 4 | 1ThinkPad123 |
+----+--------------+
That if you can't use TRY_CAST() function, if you are working on 2012+ version, I'll recommend that you use TRY_CAST() function like
SELECT Data
FROM T
WHERE Data IS NOT NULL
AND
TRY_CAST(Data AS INT) IS NULL;
Demo
Finally, I would say do not use ISNUMERIC() function because of (from docs) ...
Note
ISNUMERIC returns 1 for some characters that are not numbers, such as plus (+), minus (-), and valid currency symbols such as the dollar sign ($). For a complete list of currency symbols, see money and smallmoney (Transact-SQL).

Conversion failed on inner join

I am trying to inner join a smallint field on a varchar field. I only need the integer values but I am getting an error. The varchar field has integers and strings. I do not need the ID field only the DESC.
I get an error:
Conversion failed when converting the varchar value 'AWOL' to data type smallint.
This is what the table look like:
==================================
|ID |DESC |
|==========|=====================|
|ACAMPROCAL|acamprosate calcium |
|----------|---------------------|
|BUPROPION |bupropion |
|----------|---------------------|
|1 |Other |
|----------|---------------------|
|4 |Expired |
|----------|---------------------|
|3 |Dschg |
|----------|---------------------|
|AWOL |Absence without Leave|
==================================
Here is the query I've tried but also commented out:
SELECT
CASE_NUM, LAST_NAME, FIRST_NAME, MI, '' AS NA,
DOB, SEX, [ADDRESS], '' AS NA, CITY, STATE, ZIP
--,CONVERT(SMALLINT, CONVERT(VARCHAR(55), CDTBL1.[DESC]))
--,CASE WHEN CDTBL1.ID BETWEEN 1 AND 99999 THEN CDTBL1.[DESC]
--END AS COUNTY
FROM
CDCLIENT
INNER JOIN
CDTBL1 ON CDCLIENT.ADDR_COUNTY_ID = CDTBL1.ID
Change
'ON CDCLIENT.ADDR_COUNTY_ID = '
to
'ON CAST(CDCLIENT.ADDR_COUNTY_ID as varchar) = '
You should check the datatypes of the columns you are joining on to be sure that they are the same type.
Generally I would think an ID column would be some type of int, but the table you provided above contains varchar values in the ID column as well.
There is a precedence to the datatypes in SQL and smallint is above varchar. There is an implicit conversion taking place that is causing the failure.
More info here: https://msdn.microsoft.com/en-us/library/ms190309.aspx
This is an issue with Implicit Conversions and SQL Server chose the wrong data to convert. Typically, when you need to compare different data types, it's usually best to explicitly convert one to another.
Microsoft has a nice table on what data types are allowed to implicitly and explicitly convert between.
If you want non-negative integers, then use logic like this:
select (case when CDTBL1.[DESC] not like '%[^0-9]%'
then convert(smallint, CDTBL1.[DESC])
end)
Note that you might also want to check the length and values of the field, to be sure you don't get an error.
In SQL Server 2012+, you can simplify this using try_convert():
select try_convert(smallint, CDTBL1.[DESC])
This returns NULL if an error would occur. But, it is not available in SQL Server 2008.

How to get malformed or string type data from a numeric column in hive?

I have a column id (data type integer) containing the following records:
1
2
NULL
x
y
As hive automatically converts x and y into NULL, I'm first casting the id column to a string. Now I want count(id) where id is not from [0-9] and also not NULL. In my case, the count should be 2, but it is not working with xand y. I am also getting count of NULL's, in my example 3.
I have tried using LIKE, RLIKE and also with regexp_extract(id,'\&q=([^\&]+).
Can some one suggest me how to achieve this?
I tried something similar and it is working for me. I created an external table with your data:
CREATE EXTERNAL TABLE temp_count (count STRING) ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t' LOCATION 'user/$username/data'
Now I am running a query like this:
(Edited)
select count(*) from (select (count - count) as value from temp_count where count != 'NULL')q1 where value is NULL;
and I am getting 2 as the output.
Let me know if I am missing something here

SQL server casting string to integer checking value before casting

I have a table with a field named MINIMUM_AGE. The values stored in this field are of type nvarchar:
17 years
54 years
N/A
65 years
I would like to apply a WHERE clause on the column to check for a certain age range. To do that I need to parse out the age from the field values.
So, I think I need to select the first two characters, then cast them into an integer. Also, some fields may not contain numbers for the first two characters. Some may simply be N/A. So, I will need to check for that before casting.
Can someone explain how to accomplish this?
Here is the SQL Fiddle that demonstrates the below query:
SELECT CASE
WHEN MINIMUM_AGE <> 'N/A'
THEN CAST(LEFT(MINIMUM_AGE, 2) AS int)
ELSE 0
END
FROM MyTable
Note: the CASE expression can only return one data type. So, in the example above if the MINIMUM_AGE is N/A then it returns 0.
If you would rather have it return null, then use the following:
SELECT CASE
WHEN MINIMUM_AGE <> 'N/A'
THEN CAST(LEFT(MINIMUM_AGE, 2) AS int)
END
FROM MyTable