Opposite of SUBSTR for big query - sql

I have two tables in bigquery that can be matched on a ID. Unfortunately one of the ids has a prefix (3 digits that is not consistent)
For example, one ID is "12345" (Table two / id) and the second ID is "T1_12345" (Table one / Link_id)
When selecting from the first table I can just use SUBSTR to remove the prefix before working in the second table. However, if I want to first select in the second table with the shorter prefix and than in the first table I can't find a way to do that.
The code below is an example of what i'm working with.
I'm looking for something similar to the RIGHT or SUBSTR functions, but in reverse basically.
SELECT body from [table] where link_id in
(SELECT
id
FROM
[table_two]
WHERE
author == "Username")
This code isn't correct, but might give a clearer picture of what i'm trying to do.
SELECT body from [table] where "12345" in
(SELECT
"T1_12345"
FROM
[table_two]
WHERE
author == "Username")
Edit:
For example, if I had these two tables...
Table 1
| First_name| Link ID |
|-----------|-----------|
| James |T1_12345 |
| John |T2_12346 |
Table 2
| Surname| ID |
|-----------|--------|
| Tobbin |12345 |
| Peterson |12346 |
And I ran this query...
SELECT first_name from [table 1] where Link_ID in
(SELECT
ID
FROM
[table_two]
WHERE
Surname == "Peterson")
The output I want is: John Peterson

Below is for BigQuery Standard SQL
#standardSQL
SELECT first_name
FROM `project.dataset.table_one`
WHERE SUBSTR(Link_ID, 4) IN (
SELECT ID
FROM `project.dataset.table_two`
WHERE Surname = 'Peterson'
)
with result:
Row first_name
1 John
--
#standardSQL
SELECT CONCAT(first_name, ' ', Surname) full_name
FROM `project.dataset.table_one`
LEFT JOIN `project.dataset.table_two`
ON SUBSTR(Link_ID, 4) = ID
WHERE Surname = 'Peterson'
with result:
Row full_name
1 John Peterson
Below is for BigQuery Legacy SQL
#legacySQL
SELECT first_name
FROM (
SELECT First_name, SUBSTR(Link_ID, 4) short_ID
FROM [project:dataset.table_one]
)
WHERE short_ID IN (
SELECT ID
FROM [project:dataset.table_two]
WHERE Surname = 'Peterson'
)
--
#legacySQL
SELECT CONCAT(first_name, ' ', Surname) full_name
FROM (
SELECT First_name, SUBSTR(Link_ID, 4) short_ID
FROM [project:dataset.table_one]) t1
LEFT JOIN [project:dataset.table_two] t2
ON short_ID = ID
WHERE Surname = 'Peterson'

If you want to use in, can't you just use this?
SELECT body
FROM [table]
WHERE link_id IN (SELECT SUBSTR(id, 4)
FROM [table_two]
WHERE author = 'Username'
);

Related

Combine multiple rows with different column values into a single one

I'm trying to create a single row starting from multiple ones and combining them based on different column values; here is the result i reached based on the following query:
select distinct ID, case info when 'name' then value end as 'NAME', case info when 'id' then value end as 'serial'
FROM TABLENAME t
WHERE info = 'name' or info = 'id'
Howerver the expected result should be something along the lines of
I tried with group by clauses but that doesn't seem to work.
The RDBMS is Microsoft SQL Server.
Thanks
SELECT X.ID,MAX(X.NAME)NAME,MAX(X.SERIAL)AS SERIAL FROM
(
SELECT 100 AS ID, NULL AS NAME, '24B6-97F3'AS SERIAL UNION ALL
SELECT 100,'A',NULL UNION ALL
SELECT 200,NULL,'8113-B600'UNION ALL
SELECT 200,'B',NULL
)X
GROUP BY X.ID
For me GROUP BY works
A simple PIVOT operator can achieve this for dynamic results:
SELECT *
FROM
(
SELECT id AS id_column, info, value
FROM tablename
) src
PIVOT
(
MAX(value) FOR info IN ([name], [id])
) piv
ORDER BY id ASC;
Result:
| id_column | name | id |
|-----------|------|------------|
| 100 | a | 24b6-97f3 |
| 200 | b | 8113-b600 |
Fiddle here.
I'm a fan of a self join for things like this
SELECT tName.ID, tName.Value AS Name, tSerial.Value AS Serial
FROM TableName AS tName
INNER JOIN TableName AS tSerial ON tSerial.ID = tName.ID AND tSerial.Info = 'Serial'
WHERE tName.Info = 'Name'
This initially selects only the Name rows, then self joins on the same IDs and now filter to the Serial rows. You may want to change the INNER JOIN to a LEFT JOIN if not everything has a Name and Serial and you want to know which Names don't have a Serial

Am I on the right track..finding matches between two tables(SQL)

I'm a SQL noob and I'm wondering if someone could give me
some help with the folowing problem:
So I have two tables "Schools" and "Teachers".
the "search_key" column of the "Schools" table is one big string that
combines teachers name and other elements (example: "ENGLISH | JANE | [90, 56])
So what I'm trying to do is match the string from the column "name"
of the "teachers" table with that previous one, getting the cells that have a match.
SELECT * FROM(
SELECT substr(a.search_key, 6, instr(a.search_key, '|'))
FROM schools a
) JOIN teachers s ON a.search_key = s.search_key
This is what Ive been trying to do, substring and try and match, but no luck so far.
Any ideas?
I'm not sure why it would be more complicated than:
SELECT *
FROM schools s
INNER JOIN teachers t ON t.teacher_name LIKE '%' + s.search_key + '%'
This should get you going -
WITH SCHOOLS AS (
SELECT
'ENGLISH | JANE | [90,56]' AS SEARCH_KEY
FROM
DUAL
),TEACHER AS (
SELECT
'JANE' AS NAME
FROM
DUAL
) SELECT
S.SEARCH_KEY
FROM
SCHOOLS S,
TEACHER T
WHERE
S.SEARCH_KEY LIKE '%' || T.NAME || '%';
Output -
SEARCH_KEY
ENGLISH | JANE | [90, 56]
Another approach would be -
WITH SCHOOLS AS (
SELECT
'ENGLISH | JANE | [90,56]' AS SEARCH_KEY
FROM
DUAL
),TEACHER AS (
SELECT
'JANE' AS NAME
FROM
DUAL
) SELECT
S.SEARCH_KEY
FROM
SCHOOLS S,
TEACHER T
WHERE
TRIM(REGEXP_SUBSTR(S.SEARCH_KEY,'(\S*)(\W)',1,3)) = T.NAME;
This works if the name is always the 2nd field value in the SEARCH_KEY field de-limited by |

Oracle SQL: Transpose / rotate a table result having one row and many columns

I'm looking for a way to transpose or rotate a table in Oracle SQL. For this case there is only one row in the SELECT, but multiple columns.
Example:
SELECT
id AS "Id",
name AS "Name",
some_value AS "Favorite color"
FROM
table
WHERE
id = 5;
Result:
id | name | some_value
--- ------ -----------
5 John Orange
What I would like to see is:
Id | 5
Name | John
Favorite color | Orange
I'm aware of PIVOT, but I'm struggling to see a simple code with this case.
You can unpivot the columns to get this result as follows:
select fld, val
from (
select to_char(id) as "Id", -- convert all columns to same type
name as "Name",
some_value as "Favorite color"
from your_table
where id = 5
) unpivot(val for fld in("Id", "Name", "Favorite color"));
Use simple UNION ALL clause
SELECT 'Id' As field_name, cast( id as varchar2(100)) as Value FROM "TABLE" where id = 5
UNION ALL
SELECT 'Name' , name FROM "TABLE" where id = 5
UNION ALL
SELECT 'Favorite color' , some_value FROM "TABLE" where id = 5;
Frank Ockenfuss gave the answer I was looking for. Thanks, Frank!
However, a minor change makes changing the column names a bit more easier:
SELECT * FROM (
SELECT
TO_CHAR(id) AS id,
TO_CHAR(name) AS name,
TO_CHAR(some_value) AS fav_color
FROM my_table
WHERE id = 5
) UNPIVOT(value FOR key IN(
id AS 'Id',
name AS 'Name',
fav_color AS 'Favorite color'
));
Result:
key | value
-------------- ------
Id 5
Name John
Favorite color Orange

SELECT DISTINCT Users in Table 1 which don't exist in Table 2

I have a table with 4 columns of user information. Each table has the following columns:
Username | Full_Name | Job_Name | Current_Job_Allowed
Table 1 includes all users and the Job_Name which they have permissions to view. This means that there are multiple lines of the same username in Table 1 with different Job_Name values.
Table 2 contains a list of all possible users.
Username |Full_Name
--------------+-----------------
amunoz |Andrew Munoz
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jcarr |Jason Carr
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
Query 1 contains all users specified by a query parameter which I specify, i.e. 'KML_20160531'.
I would like to now select a DISTINCT list of all users which have a different Job_Name from the parameter I specify for Job_Name. For example Table 1 contains:
Username|Full_Name |Job_Name |Current_Job_Allowed
--------+------------+------------+----------------------
amunoz |Andrew Munoz|KML_20160531|1
jcarr |Jason Carr |KML_20160531|1
rfelipe |Ralf Felipe |KML_20140531|1
amunoz |Andrew Munoz|KML_20160431|1
I would then like to return the below when I enter 20160531 for Job_Name. This will return all possible new users for the Job_Name value I entered.
Username |Full_Name
--------------+---------------
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
This parameter query will show you which users have a Table 1 row with Job_Name matching the parameter value:
PARAMETERS which_job Text ( 255 );
SELECT t1.[Username], t1.Job_Name
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job];
So you can use that as a subquery, left join Table 2 to the subquery, and select the rows where the "right side" is Null:
PARAMETERS which_job Text ( 255 );
SELECT t2.[Username]
FROM
[Table 2] AS t2
LEFT JOIN
(
SELECT t1.[Username]
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job]
) AS sub
ON t2.[Username] = sub.[Username]
WHERE sub.[Username] Is Null;
Assuming that query returns the correct rows, add the other field you want to see to SELECT t2.[Username].
You should not need DISTINCT unless Table 2 allows duplicate Username values, or Table 1 allows more than one row with the same combination of Username and Job_Name.
You can use not in and distinct
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select distinct username
from table2 where job_name ='my_value');
and for only the job
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select username
from table2
group by username
having count(job_name) = 1
and job_name ='my_value' );
You don't actually need DISTINCT for this query at all:
select t.*
from table1 as t
where not exists (select 1
from table2 as t2
where t2.username = t.username and t2.job_name = "KML_20160531"
);
From your description of the problem, Current_Job_Allowed does not seem relevant.
This will be syntax for your problem
SELECT DISTINCT UserName
FROM Table1
WHERE UserName NOT IN (
SELECT UserName From Table2
WHERE job_name = ''
);

UPDATE and return some rows twice

I try to update and return rows. The problem is I use a nested select with UNION to get some rows twice and I want to get them returned twice. Example:
Table:
First_name | last_name | ready
-----------+-----------+------
john | doe | false
| smith | false
jane | | false
Query:
With list(name) as (
Select First_name
from table1
where First_name Not null and ready=false
union
Select last_name
from table1
where last_name Not null and ready=false
)
Select * from list
This returns:
John
jane
doe
smith
Now I want to update the rows found by the select and use update ... returning instead. But the update only returns the three affected rows, while I want it to return the rows as the select in the example does. Is there any way?
Rewrite to:
WITH cte AS (
UPDATE table1
SET ready = true
WHERE (first_name IS NOT NULL OR last_name IS NOT NULL)
AND NOT ready
RETURNING first_name, last_name
)
SELECT first_name FROM cte WHERE first_name IS NOT NULL
UNION ALL
SELECT last_name FROM cte WHERE last_name IS NOT NULL;
Same result, just shorter and faster: This query accesses table1 a single time instead of three times like in your original.
(Verify the superior performance with EXPLAIN ANALYZE on a test table.)
UNION ALL like #Clodoaldo already mentioned. UNION would eliminate duplicates, which is substantially slower (and probably wrong here).
with list(name) as (
select first_name
from table1
where first_name is not null and ready=false
union all
select last_name
from table1
where last_name is not null and ready=false
), u as (
update table1
set ready = true
where
(first_name is not null or last_name is not null)
and
not ready
)
select * from list
You need union all to have the four rows. It is is [not] null