Comparing two columns with containing one column and an addition - sql

I have an SQL table with a lot of rows. A column in this row is called Label.
The label is a combination of different numbers; example of this is
11-1234-1-1
or
11-1234-12-20
The first two positions are always a combination of 2 (11), after the first delimiter it is always 4 (1234). The third part of the label can be either 1 or 2 values (I.e it can be 1 or 12 or some other random nmr). The fourth part is random and ranging from 1-99
In this table, I also have the exact same values but in the fourth part it leads with 10 or 100 (so the fourth part receives 4 values).
Example of this is: 11-1234-12-1020
11-1234-12-20 and 11-1234-12-1020 are the same.
I want to find all these values where part B contains Part A.
The labels are found in the same column.
I have joined the columns with each other:
SELECT A.LABEL, B.LABEL
FROM TABLE A
JOIN TABLE B ON A.LABEL = B.LABEL
WHERE ??
What should my WHERE-clause be?
I have tried with LIKE and SUBSTRING but I'm missing getting values.
I.e.
WHERE A.LABEL LIKE SUBSTRING(B.LABEL,1,12) + '10' + '%'
Seeing I'm a beginner at this I'm kind of stuck. Help please :)

This should work
SELECT A.LABEL, B.LABEL FROM TABLE A
JOIN TABLE B ON
CASE WHEN LEN(RIGHT(A.LABEL, CHARINDEX('-', reverse(A.LABEL))-1)) = 1
THEN
STUFF(A.LABEL, LEN(A.LABEL) - CHARINDEX('-', reverse(A.LABEL))+1, 1, '-100')
ELSE
STUFF(A.LABEL, LEN(A.LABEL) - CHARINDEX('-', reverse(A.LABEL))+1, 1, '-10')
END = B.LABEL
So basically we find the last position of a - character in the string by reversing the string:
CHARINDEX('-', reverse(A.LABEL)
Then we insert either a 10 or a 100 at that point to compare with the other labels.

You need to do it on the join - remember you are joining two independent sets (tables) and you want the intersection where your pattern matches.
SELECT A.LABEL, B.LABEL
FROM TABLE A
INNER JOIN TABLE B ON B.LABEL LIKE A.LABEL + '%'
Cheers, T

Related

SQLite: Matching a column containig a single string to another column containing comma-separated values [duplicate]

I have a table with a column that has concatenated values like this
Table CHILD:
ChildId Values
2 x123,j455
3 f456,z789
4 m333,y567
5 x123,h888
And I have a master table MASTER that has
Table MASTER:
MainValues
x123
f456
y567
I need to get a query that'll select the following data
ChildId MainValues
2 x123
3 f456
4 y567
5 x123
Basically match value from MASTER in child values and return only the master value. How can I do this ? I have tried IN and LIKE clause matching with second table but that doesnt help much since the values are csv. Is there a way to split and match in sqlite ?
EDIT: Table and column names are fictional and intended just to explain this question better
Use a regular expression:
SELECT ChildId,MainValues FROM CHILD INNER JOIN MASTER WHERE ','||[Values]||',' like '%,'||MainValues||',%'
Also, please refrain from using keywords like values for column names...
Unfortunately SQLite doesn't have a function to find the index of a character from a string. So you have to rely on something else. Idan's method is good too but can be slower. You may try this:
SELECT c.childID, m.mainvalues
FROM CHILD c
JOIN MASTER m
WHERE m.mainvalues = substr(c.ivalues, -length(c.ivalues), 4)
OR m.mainvalues = substr(c.ivalues, 6);
I have used 4 and 6 assuming your number of characters before and after the ,. If that's not fixed you can try:
SELECT c.childID, m.mainvalues
FROM CHILD c
JOIN MASTER m
WHERE m.mainvalues = substr(c.ivalues, -length(c.ivalues), length(m.mainvalues))
OR m.mainvalues = substr(c.ivalues, length(m.mainvalues) + 2);

Getting rows for values greater by 3 numbers or letters

Am trying to come up with a query where I can return back values where the the distance between the letters could be one or more than one for the chosen letter.
For example:
I have two columns which have letters in Column A and in Column B. I want to return back with rows when column B distance is more than Column A by one or more letters.
It's not clear to me, when you say "greater" if you mean that the distance between any two letters is 2 or 3 (Column B can be alphabetically before or after Column A, by a distance of 2 or 3).. Or if Column B has to be alphabetically after Column A, by a distance of 2 or 3
Because I'm not certain what you're talking about, I present two options. Read the "if" rule and choose the one that applies to your situation, then use the query under it:
If columnA is D and columnB can be any of: A B F G
SELECT * FROM table WHERE ABS(ASCII(columna) - ASCII(columnb)) IN (2,3)
If columnA is D and columnB can be any of: F G
SELECT * FROM table WHERE ASCII(columnb) - ASCII(columna) IN (2,3)
Edit1: Per your later comment, you are now saying that the distance is not just 2 or 3 letters (the first line of your question states "2 or 3") but any number of letters distance equal to or greater than 2:
SELECT * FROM table WHERE ASCII(columnb) - ASCII(columna) >= 2
Overall the technique isn't much different to the above queries and there are many ways to specify what you want:
SELECT * FROM table
WHERE
ASCII(columnb) - ASCII(columna)
BETWEEN <some_number_here> AND <other_number_here>
Ultimately the most important thing is to note the use of ASCII function, which gives us the ascii char code of the first letter in a string:
ASCII('ABCD') => 65
And we can use maths on this to work out if a letter distance from 'A' is more than 1 etc..
Probably also worth noting that ASCII() works on single byte ascii characters. If your data is multibyte (Unicode), you might need to use ORD() instead:
Edit2: Your latest edit to the question revises the limit to "B greater than A by one or more" which is equivalent to >= 1 ..
The question seems not to have a clear spec, please treat the answer as a guide for the general technique:
--for an open ended distance, ascii chars
SELECT * FROM table WHERE ASCII(columnb) - ASCII(columna) >= <some_distance>
--for an open ended distance, unicode
SELECT * FROM table
WHERE ORD(columnb) - ORD(columna) >= <some_distance>
--for a definite range of distances (replace … appropriately)
SELECT * FROM table
WHERE ... BETWEEN <some_distance> AND <some_other_distance>
this will work indeed:
select * from table_name where ascii(col_1)+2=ascii(col_2);
You can use something like this if you need it to be exactly 2 or 3 letters greater
select Column A, ColumnB from table name where ASCII(ColumnB) - ASCII(ColumnA) in (2,3)
If you want all those rows where the the difference is equal more than 2, then use this
select Column A, ColumnB from table name where ASCII(ColumnB) - ASCII(ColumnA) >=2
this is where you can make ascii in action..
select * from SampleTable where (ASCII(sampleTable.ColumnB) - ASCII(ColumnA)) >= 2;

Concatenating codes to obtain sum

I've been for tha past 2 days trying to solve this problem but can't even seem to find the right terms to google it.
I have 3 tables.
This one, with client codes that changed:
ActualCode=111111111 PreviousCode=44444444
And these two tables with value 1 and value 2:
PreviousCode=11111111, Value1= 50,00, Value2= 0,00
ActualCode=44444444 , Value1= 0,00, Value2 = 50,00
I need to sum the values for each relation of Previous and Actual codes from the first table.
I.E.
For
ActualCode=11111111, PreviousCode=44444444
I need to be able to get:
Code=11111111 Value1=50,00 Value2=50,00
Looking forward for your answer :D
Thanks,
P
You can join the tables and sum the values:
select c.actualcode,
sum(ac.value1) + sum(pc.value1) as value1,
sum(ac.value2) + sum(pc.value2) as value2
from codes c
join actualcodes ac on c.actualcode = ac.actualcode
join previouscodes pc on c.previouscode = pc.previouscode
group by c.actualcode;
Rextester Demo
If you could have values in the main table that don't have corresponding rows in the values tables, then you should use outer joins instead.

substring and trim in Teradata

I am working in Teradata with some descriptive data that needs to be transformed from a gerneric varchar(60) into the different field lengths based on the type of data element and the attribute value. So I need to take whatever is in the Varchar(60) and based on field 'ABCD' act on field 'XYZ'. In this case XYZ is a varchar(3). To do this I am using CASE logic within my select. What I want to do is
eliminate all occurances of non alphabet/numeric data. All I want left are upper case Alpha chars and numbers.
In this case "Where abcd = 'GROUP' then xyz should come out as a '000', '002', 'A', 'C'
eliminate extra padding
Shift everything Right
abcd xyz
1 GROUP NULL
2 GROUP $
3 GROUP 000000000000000000000000000000000000000000000000000000000000
4 GROUP 000000000000000000000000000000000000000000000000000000000002
5 GROUP A
6 GROUP C
7 GROUP r
To do this I have tried TRIM and SUBSTR amongst several other things that did not work. I have pasted what I have working now, but I am not reliably working through the data within the select. I am really looking for some options on how to better work with strings in Teradata. I have been working out of the "SQL Functions, Operators, Expressions and Predicates" online PDF. Is there a better reference. We are on TD 13
SELECT abcd
, CASE
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WHEN abcd= 'GROUP'
THEN(
CASE
WHEN SUBSTR(tx.abcd,60, 4) = 0
THEN (
SUBSTR(tx.abcd,60, 3)
)
ELSE
TRIM (TRAILING FROM tx.abcd)
END
)
END AS abcd
FROM db.descr tx
WHERE tx.abcd IS IN ( 'GROUP')
The end result should look like this
abcd xyz
1 GROUP 000
2 GROUP 002
3 GROUP A
4 GROUP C
I will have to deal with approx 60 different "abcd" types, but they should all conform to the type of data I am currently seeing.. ie.. mixed case, non numeric, non alphabet, padded, etc..
I know there is a better way, but I have come in several circles trying to figure this out over the weekend and need a little push in the right direction.
Thanks in advance,
Pat
The SQL below uses the CHARACTER_LENGTH function to first determine if there is a need to perform what amounts to a RIGHT(tx.xyz, 3) using the native functions in Teradata 13.x. I think this may accomplish what you are looking to do. I hope I have not misinterpreted your explanation:
SELECT CASE WHEN tx.abcd = 'GROUP'
AND CHARACTER_LENGTH(TRIM(BOTH FROM tx.xyz) > 3
THEN SUBSTRING(tx.xyz FROM (CHARACTER_LENGTH(TRIM(BOTH FROM tx.xyz)) - 3))
ELSE tx.abcd
END
FROM db.descr tx;
EDIT: Fixed parenthesis in SUBSTRING

Joining/Querying Tables by a Substring

Currently I have a query which is partly based on a join on two tables according to two number columns within them.
Say one table has a number like 123456789999 (NUM1)
And the other table has a number ranging from 1 - 9999 (NUM2)
I want to pull out the records which have 'NUM2' within the 5th - 8th digits of 'NUM1'
Currently I am doing something like this,
FROM Table1 AS T INNER JOIN Table2 AS S
ON SUBSTRING(T.num1, 5, 4) = S.num2
I know it should be retrieving approx 100 records, but I only get 8. I believe it to be because of the small ranges within number two. Where have I gone wrong? OR how could my code be made more robust/effective?
You need to use CAST like this:
FROM Table1 AS T INNER JOIN Table2 AS S
ON CAST(SUBSTRING(T.num1, 5, 4) AS INT) = S.num2
SEE THIS FIDDLE
For more info see SQL SERVER – Convert Text to Numbers (Integer) – CAST and CONVERT
try this:
Since the datatype of NUM2 is int, 0001 will be considered as just 1
so try this:
FROM Table1 AS T INNER JOIN Table2 AS S
ON cast(SUBSTRING(T.num1, 5, 4) as int) = S.num2