SQL - Return all rows for ID where one row meets condition A,B,or C - sql

I'm trying to return all rows for a particular IDs where a condition is met in any one of the rows tied to those IDs. Pardon me being a newbie to SQL... Example below:
ID * Line * # *
12 * 1 * A *
12 * 2 * B *
12 * 3 * X *
12 * 4 * Y *
15 * 1 * A *
15 * 2 * B *
15 * 3 * C *
Not sure what the code would be other than my select and condition = (X, Y, or Z) to return:
ID * Line * # *
12 * 1 * A * <-- doesn't include X, Y, or Z but is part of the ID which
12 * 2 * B * <-- has X in another row of that ID
12 * 3 * X *
12 * 4 * Y *
I'm wanting to pull all row records despite not meeting the condition as long as they're part of the ID that has a row that meets the condition.
Thanks for the help!
* Edit: Including code attempted*
SELECT ID
,LINE
,#
WHERE ID,
IN (
SELECT ID
WHERE # IN ('X','Y','Z'))
Results:
ID LINE #
12 3 X
12 4 Y
What I need:
ID LINE #
12 1 A
12 2 B
12 3 X
12 4 Y
I almost feel like I need to create a temp table of ID & LINE using my condition of IN('X','Y','Z') and then inner join on ID for all LINE(s) not X,Y,Z. I think that may work, but I haven't learned how to use temp tables yet. I'm a little troubled because I'm using a query, which I've simplified a ton here, where I'm selecting 18 fields that join in 7 other tables. I think this is just complicating my understanding of the subquery, not so much the subquery being affected by that.
Thanks all for the help and answers so far!

You can use a subquery and IN for this.
Select *
From YourTable
where ID in (select ID from YourTable where # in ('X','Y','Z'))
Just a note, there is no 12 * 4 * C * in your data but I think it's just a type-o in your results and should be 12 * 4 * Y *

Besides the subquery approach you might also try an OLAP-function (Depending on the actual data this might be better or worse, of course)
In Teradata you can apply QUALIFY:
Select *
From YourTable
qualify -- check if any row with the same ID has X/Y/Z
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) = 1
In SQL Server you have to use a Derived Table/CTE:
Select *
From
( Select *,
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) as flag
from YourTable
) as dt
where flag = 1

Related

How to update each column one at time for each row in snowflake

Suppose I have 10 columns in my table and I want to update each column but one at a time for each row up to 10 rows.
if table is like
1,2,3
4,5,6
7,8,9
I want to update it like
x,2,3
4,y,6
7,8,z
Columns can be of any count so need dynamic approach. Also sometimes need to exclude some columns.
I tried to see if I can update row based on row id but there is no such option available as row id. I don't wanna change design of table to include a counter column.
you can use window function to assign a a row id and based on that :
with cte as (
select * from (
select * , row_number() over (order by id) rn
from tablename
) t ) ;
update t
set col1 = case when rn = 1 then <updatevalue> else col1 end
, col2 = case when rn = 2 then <updatevalue> else col2 end
, col3 = case when rn = 3 then <updatevalue> else col3 end
, ...
from tablename t
join cte on cte.id = t.id
The requirement "Columns can be of any count so need dynamic approach" looks like as a try to implement matrix as a table.
Alternative approach could be usage of ARRAY type and storing entire structure as single "cell" in the table.
CREATE OR REPLACE TABLE t
AS
SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(1,2,3),
ARRAY_CONSTRUCT(4,5,6),
ARRAY_CONSTRUCT(7,8,9)) c
UNION ALL
SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(10,20,30),
ARRAY_CONSTRUCT(40,50,60),
ARRAY_CONSTRUCT(70,80,90)) c;
SELECT *
FROM t;
/*
C
[[1,2,3],[4,5,6],[7,8,9]]
[[10,20,30],[40,50,60],[70,80,90]]
*/
Accessing elements:
SELECT c[0][0], c[0][1], c[0][2],
c[1][0], c[1][1], c[1][2],
c[2][0], c[2][1], c[2][2]
FROM t;
/*
C[0][0] C[0][1] C[0][2] C[1][0] C[1][1] C[1][2] C[2][0] C[2][1] C[2][2]
1 2 3 4 5 6 7 8 9
10 20 30 40 50 60 70 80 90
*/
Update:
UPDATE t
SET c = ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('x' , c[0][1], c[0][2])
,ARRAY_CONSTRUCT(c[1][0], 'y' ,c[1][2])
,ARRAY_CONSTRUCT(c[2][0], c[2][1] , 'z' )
);
SELECT * FROM t;
/*
C
[["x",2,3],[4,"y",6],[7,8,"z"]]
[["x",20,30],[40,"y",60],[70,80,"z"]]
*/
More robust transformations could be performed via user-defined functions.

INTERSECT ALL not working on PostgreSQL 11

The PostgreSQL operation INTERSECT ALL does not seem to work. What am I missing?
The following query returns just one row containing the value two, but I am expecting two with the value as I am using intersect all.
(
(select 1 as z)
union all
(select 2 as z)
union all
(select 2 as z)
)
intersect all
(select 2 as z)
Does anyone have a guess?
There is only one row (with a value of 2 for the column z) in the second operand to INTERSECT ALL so only that one can be used to find a matching partner in the other operand.
Add a second row to the second operand of the INTERSECT ALL and you'll have two rows in the result.
(SELECT 1 z
UNION ALL
SELECT 2 z
UNION ALL
SELECT 2 z)
INTERSECT ALL
(SELECT 2 z
UNION ALL
SELECT 2 z);
Or you might instead want a join.
SELECT *
FROM (SELECT 1 z
UNION ALL
SELECT 2 z
UNION ALL
SELECT 2 z) x1
INNER JOIN (SELECT 2 z) x2
ON x2.z = x1.z;
This is how it works in all version, not just 11.
The single value of '2' on the right side of the INTERSECT ALL is consumed upon matching, and can't match multiple times.
Do you really want WHERE EXISTS (..) instead?

Teradata SUBSTRING Index Out of Bounds

This query works:
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
But the following query (with WHERE clause added) does not execute.
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
The query above throws
SELECT Failed. 2663: SUBSTR: string subscript out of bounds in table_name.column_name
However, this following one works (original SELECT is nested)
SELECT *
FROM (
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
) A
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
Why is that so? I am using SQL assistant to execute the queries but I doubt it is of relevance.
Try to change (maybe error is caused when column_name's lenght is less then 6):
SELECT
TOP 100 CASE WHEN CHARACTER_LENGTH(column_name)>5
THEN SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5)
ELSE NULL END AS X
FROM db_name.table_name

Using IN with convert in sql

I would like to use the IN clause, but with the convert function.
Basically, I have a table (A) with the column of type int.
But in the other table (B) I Have values which are of type varchar.
Essentially, what I am looking for something like this
select *
from B
where myB_Column IN (select myA_Columng from A)
However, I am not sure if the int from table A, would map / convert / evaluate properly for the varchar in B.
I am using SQL Server 2008.
You can use CASE statement in where clause like this and CAST only if its Integer.
else 0 or NULL depending on your requirements.
SELECT *
FROM B
WHERE CASE ISNUMERIC(myB_Column) WHEN 1 THEN CAST(myB_Column AS INT) ELSE 0 END
IN (SELECT myA_Columng FROM A)
ISNUMERIC will be 1 (true) for Decimal values as-well so ideally you should implement your own IsInteger UDF .To do that look at this question
T-sql - determine if value is integer
Option #1
Select * from B where myB_Column IN
(
Select Cast(myA_Columng As Int) from A Where ISNUMERIC(myA_Columng) = 1
)
Option #2
Select B.* from B
Inner Join
(
Select Cast(myA_Columng As Int) As myA_Columng from A
Where ISNUMERIC(myA_Columng) = 1
) T
On T.myA_Columng = B.myB_Column
Option #3
Select B.* from B
Left Join
(
Select Cast(myA_Columng As Int) As myA_Columng from A
Where ISNUMERIC(myA_Columng) = 1
) T
On T.myA_Columng = B.myB_Column
I will opt third one. Reason is below mentioned.
Disadvantages of IN Predicate
Suppose I have two list objects.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Using Contains, it will search for each List-1 item in List-2 that means iteration will happen 49 times !!!
You can also use exists caluse,
select *
from B
where EXISTS (select 1 from A WHERE CAST(myA_Column AS VARCHAR) = myB_Column)
You can use below query :
select B.*
from B
inner join (Select distinct MyA_Columng from A) AS X ON B.MyB_Column = CAST(x.MyA_Columng as NVARCHAR(50))
Try it by using CAST()
SELECT *
FROM B
WHERE CAST(myB_Column AS INT(11)) IN (
SELECT myA_Columng
FROM A
)

MULTIPLE SELECTS - SAME TABLE

Here goes...
I have 5 records in a table entitled MyTable with a single field called Se. The 5 records contain the following values (1-5):
se=1 se=2 se=3 se=4 se=5
I want to have the records returned to me as follows:
SELECT * FROM MyTable WHERE se >= 3
UNION
SELECT * FROM MyTable WHERE se < 3
ORDER BY se ASC
My objective is to get records returned as:
3,4,5,1 2
but naturally I get...
1,2,3,4,5
Can you help me? Can MSSQL Server even do this?
Thanks in advance for any assistance.
Try this
SELECT 1, * FROM MyTable WHERE se >= 3
UNION ALL
SELECT 2, * FROM MyTable WHERE se < 3
ORDER BY 1, se ASC
You can use a single select and a condition in the sorting:
select *
from MyTable
order by (case when se >= 3 then 0 else 1 end), se
(The parentheses around the case is not needed, I just added them to make the code clearer.)
If you're looking for a hack for that specific scenario:
SELECT * FROM MyTable ORDER BY (se + 2) % 5
Example on PostgreSQL:
$ WITH MyTable(se) AS (VALUES
$ (1), (2), (3), (4), (5)
$ )
$ SELECT * FROM MyTable ORDER BY (se + 2) % 5;
se
----
3
4
5
1
2
(5 rows)