I'm trying to join two tables together based on an ID column. The join is not working successfully because I cannot join a varchar column on an integer column, despite using cast().
In the first table, the ID column is character varying, in the format of: XYZA-123456.
In the second table, the ID column is simply the number: 123456.
-- TABLE 1
create table fake_receivers(id varchar(11));
insert into fake_receivers(id) values
('VR2W-110528'),
('VR2W-113640'),
('VR4W-113640'),
('VR4W-110528'),
('VR2W-110154'),
('VMT2-127942'),
('VR2W-113640'),
('V16X-110528'),
('VR2W-110154'),
('VR2W-110528');
-- TABLE 2
create table fake_stations(receiver_sn integer, station varchar);
insert into fake_stations values
('110528', 'Baff01-01'),
('113640', 'Baff02-02'),
('110154', 'Baff03-01'),
('127942', 'Baff05-01');
My solution is to split the string at the dash, take the number after the dash, and cast it as an integer, so that I may perform the join:
select cast(split_part(id, '-', 2) as integer) from fake_receivers; -- this works fine, seemingly selects integers
However, when I actually attempt to perform the join, I'm getting the following error, despite using an explicit cast:
select cast(split_part(id, '-', 2) as integer), station
from fake_receivers
inner join fake_locations
on split_part = fake_locations.receiver_sn -- not recognizing split_part cast as integer!
>ERROR: operator does not exist: character varying = integer
>Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Strangely enough, I can perform this join with my full dataset (a queried result set shows up) but I then can't manipulate it at all (e.g. sorting, filtering it) - I get an error saying ERROR: invalid input syntax for integer: "UWM". The string "UWM" appears nowhere in my dataset or in my code, but I strongly suspect it has to do with the split_part cast from varchar to integer going wrong somewhere.
-- Misc. info
select version();
>PostgreSQL 10.5 on x86_64-apple-darwin16.7.0, compiled by Apple LLVM version 9.0.0 (clang-900.0.39.2), 64-bit
EDIT: dbfiddle exhibiting behavior
You need to include your current logic directly in the join condition:
select *
from fake_receivers r
inner join fake_stations s
on split_part(r.id, '-', 2)::int = s.receiver_sn;
Demo
Related
I've been trying to run this simple query but I'm having this error:
Select I.RecipeId,
I.Name
from Ingredient as I
inner join
Recipe as R
on
I.RecipeId = CAST( R.id AS nvarchar(MAX))
where
I.RecipeId >= 241956
AND
I.RecipeId <= 242018
The conversion of the varchar value '160088961736173116 ' overflowed an int column.
You have a fundamental problem in your data model. In therecipe table, the id is bigint. In the ingredient column, the type is varchar(30).
Fix the data model! The referring types should be the same type. This is a very important principle when using relational databases. The columns that represent relationships need to be compatible.
I would start with:
alter table ingredient alter column recipeId bigint;
If this doesn't work due to a conversion error, then you need to find the offending value. You can try:
select *
from ingredient
where recipeId like '%[^0-9]%';
This should find the offending values.
RecipeID is a literal value that's being compared against a hard-coded integer 241956. This forces the motor to convert all values stored in RecipeID to an integer value. Since there's at least one value (160088961736173116) that exceeds the max value for an integer, it fails.
You can explicitly convert your hard-coded value to a BIGINT which can hold higher values, including 160088961736173116. Try this out:
Select
I.RecipeId,
I.Name
from
Ingredient as I
inner join Recipe as R on CONVERT(BIGINT, I.RecipeId) = R.id
where
I.RecipeId BETWEEN CONVERT(BIGINT, 241956) AND CONVERT(BIGINT, 242018)
I am receiving the above error when using the below query
SELECT b.*,E.SSNO
FROM [SRV-RVS].[dbo].[CARD] b
INNER JOIN [SRV-RVS].dbo.EMP e
on b.EMPID=E.SSNO
WHERE E.SSNO LIKE 't%'
I am trying to join both tables, here my EMPID is same as SSNO but it got a character in the begining .
Hope you got it
Regards
You could convert EMPID to a varchar and add a 'T' character in the join clause.
SELECT b.*,E.SNO
FROM [SRV-RVS].[dbo].[CARD] b INNER JOIN [SRV-RVS].dbo.EMP e ON ('T' + REPLACE(STR(CAST(b.EMPID as varchar(9)), 9), SPACE(1), '0')) = E.SSNO
WHERE E.SNO LIKE 't%'
Additional notes
I guessed on the length of the varchar but SSN is generally always 9 digits. If you are storing mask/space characters like - in your SSNO column this code will not work.
There is code in there to left 0 pad the id for SSN numbers that start with 0 as an int to string will not automatically pad 0.
If there data sets are large this could cause performance problems.
Really the schema should never have converted SSN to an int (numeric) to begin with. It should have stayed as a varchar field and ideally not set as a primary key on another table either.
Again (continue from last bullet), change the schema or add a computed column. This is currently a poor design.
Fix your table so you can do the join. The rest of this answer assumes you are using SQL Server (based on the syntax of the code in the question).
I would encourage you to do:
alter table emp
add column ssno as ( cast(stuff(empid, 1, 1, '') as int) );
Or to whatever type matches. You can even build an index on this.
Then your code doesn't have to remember business rules about the relationship between empid and ssno.
Join on table by extracting numeric values from string, so it would match on Id Column. It should return result as long u dint have any other surprises in the rowdata. This works by eliminating character in the beginning.
SELECT b.*,E.SSNO
FROM [SRV-RVS].[dbo].[CARD] b
INNER JOIN [SRV-RVS].dbo.EMP e
on right(b.EMPID, len(b.EMPID) - (PatIndex('%[0-9]%', b.EMPID )-1) )
=right(E.SSNO, len(E.SSNO) - (PatIndex('%[0-9]%', E.SSNO )-1) )
Here's where I am:
TABLE1.ITM_CD is VARCHAR2 datatype
TABLE2.ITM_CD is NUMBER datatype
Executing left join TABLE2 on TABLE1.ITM_CD = TABLE2.ITM_CD yields ORA-01722: invalid number error
Executing left join TABLE2 on to_number(TABLE1.ITM_CD) = TABLE2.ITM_CD also yields ORA-01722: invalid number error.
-- I suspect this is because one of the values in TABLE1.ITM_CD is the string "MIXED"
Executing left join TABLE2 on TABLE1.ITM_CD = to_char(TABLE2.ITM_CD) successfully runs, but it returns blank values for the fields selected from TABLE2.
Here is a simplified version of my working query:
select
A.ITM_CD
,B.COST
,B.SIZE
,B.DESCRIPTION
,A.HOLD_REASON
from
TABLE1 a
left join TABLE2 b on a.ITM_CD = to_char(b.ITM_CD)
This query returns a list of item codes and hold reasons, but just blank values for the cost, size, and descriptions. And I did confirm that TABLE2 contains values for these fields for the returned codes.
UPDATE: Here are pictures with additional info.
I selected the following info from ALL_TAB_COLUMNS--I don't necessarily know what all fields mean, but thought it might be helpful
TABLE1 sample data
TABLE2 sample data
You can convert the TABLE1.ITM_CD to a number after you strip any leading zeros and filter out the "MIXED" values:
select A.ITM_CD
,B.COST
,B.SIZE
,B.DESCRIPTION
,A.HOLD_REASON
from ( SELECT * FROM TABLE1 WHERE ITM_CD <> 'MIXED' ) a
left join TABLE2 b
on TO_NUMBER( LTRIM( a.ITM_CD, '0' ) ) = b.ITM_CD
This is a SQL (and really a database) problem, not PL/SQL. You will need to fix this - fight your bosses if you have to. Item code must be a Primary Key in one of your two tables, and a Foreign Key in the other table, pointing to the PK. Or perhaps Item code is PK in another table which you didn't show us, and Item code in both the tables you showed us should be FK pointing to that PK.
You don't have that arrangement now, which is exactly why you are having all this trouble. The data type must match (it doesn't now), and you shouldn't have values like 'MIXED' - unless your business rules allow it, and then the field should be VARCHAR2 and 'MIXED' should be one of the values in the PK column (whichever table that is in).
In your case, the problem is that the codes in VARCHAR2 format start with a leading 0, so if you compare to the numbers converted to strings, you never get a match (and in the outer join, the match is always assumed to be to NULL).
Instead, when you convert your numbers to strings, add leading zero(s) like this:
...on a.ITM_CD = TO_CHAR(b.ITM_CD, '099999')
You can trim leading zeros from your string column.
select
A.ITM_CD
,B.COST
,B.SIZE
,B.DESCRIPTION
,A.HOLD_REASON
from
TABLE1 a
left join TABLE2 b on trim(LEADING '0' FROM a.ITM_CD ) = to_char(b.ITM_CD)
I want this query to return all ids and associated emails that are NOT returned by query #2:
select my_table.id, my_table.email
from my_table join another_table on my_table.id=another_table.mytable_id
where my_table.id not in (select array (select my_table.id
from my_table join yetanother_table
on my_table.id = yetanother_table.mytable_id
where yetanother_table.time1 between '2015-01-26T08:00:00.000Z'
and '2015-02-02T07:59:59.999Z'
group by my_table.id))
When I run it, I get the following error near the select in line 3:
operator does not exist: integer = integer[]
You might need to add explicit type casts.
I tried casting the array to integer and got this:
cannot cast type integer[] to integer
I also tried casting both my_table.id and the array to varchar. That got rid of the error but returned fewer rows than I expected!
First questions: Why can't I cast integer[] to integer? What is the default data type of an array? Is there a cleaner way to get rid of the "operator does not exist" error than casting both my_table.id and the array to varchar?
And following up: now I'm wondering why I'm getting too few rows. Does it look like the way I wrote it will return what I want? i.e. all ids that are not returned by query #2?
Another constraint - I need to do everything with one statement.
Once you remove the misplaced array constructor, you get a working query.
The IN and NOT IN constructs require a plain subquery to the right yielding matching types - not an array.
The query is still twisted and inefficient, though. Use instead:
select m.id, m.email
from my_table m
join another_table a on a.mytable_id = m.id
left join yetanother_table y on y.mytable_id = m.id
and y.time1 >= '2015-01-26 08:00'
and y.time1 < '2015-02-02 08:00'
where y.mytable_id IS NULL;
More information:
Select rows which are not present in other table
You need to get rid of the select array. The IN operator is looking for a single column query result, not an array.
I have two tables already created:
create table movies(id integer, name text, score integer);
create table cast(movie_id integer, cast_id integer, cast_name text);
I need the first 10 (distinct, alphabetically by cast_name) cast members and their average movie scores, so I tried:
select movie_id,cast_id,cast_name,id,score from cast,movies
where movies.id=cast.movie_id and cast_name in
(select distinct cast_name from cast order by cast_name limit 10);
But then I got an error message: near "." : syntax error
After that, I tried to make it simpler:
select cast_name, score from cast,movies where movies.id=cast.movie_id;
I still got the same error.
I guess this might be because '.' is a special command in sqlite3, but cannot figure out how to solve this problem.
Any help will be appreciated.
cast is a reserved word. The list of reserved words is here.
select cast_name, score
from `cast` c join
movies m
on m.id = c.movie_id;
You can escape it using backticks or double quotes. This query uses table aliases to simplify the query and more modern syntax for the join.
You cannot specify a column using . notation just call the columns by their name not the tablename.columnname