Select data from table with update if particular row does not exist - sql

I have a simple table TBL consisting of two columns Err_type and Val.
Need to select all date from it. Seems to be simple, but it gets uglier when particular row does not exist.
with cte as (
select TBL.Err_type, TBL.Val from TBL
where TBL.Err_type = 4 or TBL.Err_type = 2
)
select * from cte
There are possibilities that "4" or "2" might not exist in given datetime range. So i need to insert the missing row ("4" or "2" for Err_type and "0" for Val) and then get the table.
e.g.
Err_type | Val
---------------
4 | 50
2 | 0
instead of
Err_type | Val
---------------
4 | 50

Use following sql that will check for existence of record and if not found then insert.
WITH cte
AS (SELECT *
FROM TBL
WHERE TBL.Err_Type IN ( 4, 2 )
UNION
SELECT 4,
0
WHERE NOT EXISTS (SELECT 1
FROM TBL
WHERE TBL.Err_Type = 4)
UNION
SELECT 2,
0
WHERE NOT EXISTS (SELECT 1
FROM TBL
WHERE TBL.Err_Type = 2))
SELECT *
FROM cte

Related

How to use WHERE query at same column in same table?

I have this data.
tbl_data
id value
1 A
1 B
1 C
2 A
2 C
I want to select id which have 'A' and 'B' as value.
SELECT id FROM tbl_data WHERE value = 'A' AND value = 'B'
But it returns zero result.
How to make it to return id 1 ?
select id from table
where
value in ('A', 'B')
group by id
having count( distinct value ) = 2
An alternative to the solution of #OTAR is to use a CTE
CREATE TEMPORARY TABLE t ( ID INT, value TEXT);
INSERT INTO t VALUES
(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'C'),(2,'F');
WITH j AS (
SELECT id,array_agg(value) AS arr
FROM t GROUP BY id)
SELECT * FROM j
WHERE arr #> ARRAY['A','B'];
id | arr
----+---------
1 | {A,B,C}
(1 Zeile)
You can simply execute the following:
SELECT min(id) FROM table WHERE value in ('A','B');

How to pass list of columns from one table to another table in bigquery

Following is my Table-A
Column_Name Flag
----------- ----
col-A 1
col-B 1
col-C 1
col-D 2
Columns col-A, col-B, col-C, col-D are present in Table-B as follows
ID col-A col-B col-C col-D
1 a b c d
I want to write a query something like
select (select column_name from table-A where flag = 1) from table-B
Above query should translate to something like
select col-A, col-B, col-C from table-B.
I tried the following:
select Array_to_String(Array(select column_name from table-A where flag = 1)) from table-B
But the above function Array_to_string gives me the list of columns as a single string.
Below is example for BigQuery Standard SQL
I've added yet another table (tableC) to have association between group)flag) and respective action. Overall it is simplified (as your question is) but shows possible approach - that you still will need to apply to your specific use case
Note, based on your comments - I assume you apply aggregation on columns within the rows
#standardSQL
WITH `tableA` AS (
SELECT 'colA' Column_Name, 1 Flag UNION ALL
SELECT 'colB', 1 UNION ALL
SELECT 'colC', 1 UNION ALL
SELECT 'colD', 2 UNION ALL
SELECT 'colE', 2
), `tableB` AS (
SELECT 1 id, 1 colA, 2 colB, 3 colC, 4 colD, 5 colE UNION ALL
SELECT 2, 5, 6, 7, 8, 9
), `tableC` AS (
SELECT 1 flag, 'SUM' action UNION ALL
SELECT 2, 'BIT_AND'
)
SELECT id,
CASE action
WHEN 'SUM' THEN SUM(CAST(val AS INT64))
WHEN 'BIT_AND' THEN BIT_AND(CAST(val AS INT64))
END val
FROM (
SELECT id,
SPLIT(kv, ':')[OFFSET(0)] col,
SPLIT(kv, ':')[OFFSET(1)] val
FROM `tableB` t,
UNNEST(SPLIT(REGEXP_REPLACE(TO_JSON_STRING(t), r'^{|}$|"', ''))) kv
)
JOIN `tableA` ON col = Column_Name AND flag = 1 -- set flag here
JOIN `tableC` USING(flag)
GROUP BY id, action
You just need to set value for flag
If you set flag = 1 - result will be SUM of columns
Row id val
1 1 6
2 2 18
If you set flag = 2 - result will be BIT_AND applied to "survived" columns
Row id val
1 1 4
2 2 8

SQL search for minimum values related to a table of duplicates

I have a SQL View that pumps out the following table, two columns
Column 1 Column 2
ID Value
1 10
1 12
1 15
2 3
2 6
I'd like to search for where record 1 returns it's minimum value (10) and record 2 returns it's minimum value (3)
Any help greatly appreciated.
SELECT
id
, MIN(value)
FROM yourview
GROUP BY id
You could try something like:
select [id], min([value]), max([value])
from viewname
group by id
order by id
The square brackets are SQL Server syntax to sidestep keywords; your database syntax may differ. What database are you using?
You could use Row_Number() OVER ().
; WITH cte AS (
SELECT id1 = 1, value = 10 UNION ALL
SELECT id1 = 1, value = 12 UNION ALL
SELECT id1 = 1, value = 15 UNION ALL
SELECT id1 = 2, value = 3 UNION ALL
SELECT id1 = 2, value = 6
)
SELECT z.id1, z.value
FROM (
SELECT id1, value, ROW_NUMBER() OVER ( PARTITION BY id1 ORDER BY value ASC ) AS rn
FROM cte
) z
WHERE z.rn = 1
Returns:
id1 value
1 10
2 3

SQL: Making a 'computation row'

I have a table that looks like this
TYPE | A | B | C | ... | Z
one | 4 | 4 | 4 | ... | 4
two | 3 | 2 | 2 | ... | 1
And I wanted to insert a row with a computation (row one minus row two):
TYPE | A | B | C | ... | Z
one | 4 | 4 | 4 | ... | 4
two | 3 | 2 | 2 | ... | 1
delta| 1 | 2 | 2 | ... | 3
I was thinking of a SQL command that looks like
(select A from table where type=one) - (select A from table where type=two)
Down side is, it's too long and I also have to do that for all the columns (A-Z) and that's quite a lot.
I'm sure there's a more elegant way of doing this.
PS:
The sequence of my code looks like this btw:
// I'm inserting the data from a RawTable to a TempTable
INSERT one
INSERT two
INSERT delta
INSERT three
INSERT four
INSERT delta
...
INSERT onehundredone
INSERT onehundredtwo
INSERT delta
I have added an ID column with identity to your temp table. You can use that to figure out what rows should be grouped.
create table YourTable
(
ID int identity primary key,
[TYPE] varchar(20),
A int,
B int,
C int
)
insert into YourTable ([TYPE], A, B, C)
select 'one', 4, 4, 4 union all
select 'two', 3, 2, 2 union all
select 'three', 7, 4, 4 union all
select 'four', 3, 2, 2 union all
select 'five', 8, 4, 4 union all
select 'six', 3, 2, 2
select T.[TYPE], T.A, T.B, T.C
from
(
select
T.ID,
T.[TYPE],
T.A,
T.B,
T.C
from YourTable as T
union all
select
T2.ID,
'delta' as [TYPE],
T1.A-T2.A as A,
T1.B-T2.B as B,
T1.C-T2.C as C
from YourTable as T1
inner join YourTable as T2
on T1.ID = T2.ID-1 and
T2.ID % 2 = 0
) as T
order by T.ID, case T.[TYPE] when 'delta' then 1 else 0 end
Result:
TYPE A B C
-------------------- ----------- ----------- -----------
one 4 4 4
two 3 2 2
delta 1 2 2
three 7 4 4
four 3 2 2
delta 4 2 2
five 8 4 4
six 3 2 2
delta 5 2 2
Sorting on column C from first row in group:
select T.[TYPE], T.A, T.B, T.C
from
(
select
T1.ID,
T1.[TYPE],
case T1.ID % 2 when 1 then T1.C else T2.C end as Sortorder,
T1.A,
T1.B,
T1.C
from YourTable as T1
left outer join YourTable as T2
on T1.ID = T2.ID+1
union all
select
T2.ID,
'delta' as [TYPE],
T1.C as Sortorder,
T1.A-T2.A as A,
T1.B-T2.B as B,
T1.C-T2.C as C
from YourTable as T1
inner join YourTable as T2
on T1.ID = T2.ID-1 and
T2.ID % 2 = 0
) as T
order by T.Sortorder, T.ID, case T.[TYPE] when 'delta' then 1 else 0 end
I'm not aware of any way to do this "easily" (i.e. without having to specify every column), I can't come up with any way to do it easily, so I'll go on the record as saying that it can't be done. Easily.
The non-easy way would be to build dynamic code--something that loops through the database metadata, builds a string containing the statement(s) to execute your desired routine column by column, and then execute that string. You really want to avoid this whenever possible.
One shortcut, if you just need to build a procedure or function that does this (i.e. build once run many), you could copy the list of columns into a spreadsheet (Excel), build out the highly-repetitive statements using forumlas that reference the column names, and then copying the results back. (This is much simpler to do than it is to explain.)
I have no idea why you're doing this, but the way I'd approach it is:
insert into table
select 'delta',
t1.a - t2.a,
t1.b - t2.b
.....
from table t1,
table t2
where t1.type = 'one'
and t2.type = 'two'
You would have to run this query immediately after inserting "one" and "two", then re-run it after inserting "three" and "four". Nasty nasty nasty.
If you can re-name the columns in some way, or create a numerical column, you could run it in a single query.
When you replace one for 1, two for 2, and so on, then maybe this sql could work:
INSERT INTO PodMays
SELECT
"Delta", A.A-B.A, A.B-B.B, A.C-B.C, A.D-B.D, A.E-B.E
FROM
(
SELECT TOP 1
*
FROM
(SELECT TOP 2 * FROM PodMays WHERE Type <> "Delta" ORDER BY Type DESC)
ORDER BY
Type ASC
) AS A,
(
SELECT TOP 1
*
FROM
(SELECT TOP 2 * FROM PodMays WHERE Type <> "Delta" ORDER BY Type DESC)
ORDER BY
Type DESC
) AS B

sql range operator

Is there a SQL construct or a trick to do something like:
SELECT i WHERE i BETWEEN 0 AND 10;
?
My first idea is to create a temporary table such as:
CREATE TABLE _range ( i INT PRIMARY KEY );
and fill it
INSERT INTO _range VALUES 0;
INSERT INTO _range VALUES 1;
etc.
Is there a better way?
UPDATE:
I use sqlite in this particular case but i am interested in general answer.
What DB are you using? The between operator generally is legal syntax for SQL. We use it all the time for date ranges.
from http://www.w3schools.com/Sql/sql_between.asp
SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
Very interesting question. Here's an ugly hack that probably is useless unless your ranges are really that small...
select * from (
select 1 as num UNION
select 2 as num UNION
select 3 as num UNION
select 4 as num UNION
select 5 as num UNION
select 6 as num UNION
select 7 as num UNION
select 8 as num UNION
select 9 as num UNION
select 10 as num
) t ;
+-----+
| num |
+-----+
| 1 |
| 2 |
....
| 9 |
| 10 |
+-----+
10 rows in set (0.00 sec)
Edit: Ok, so I got to thinking why not use cross joins. So, here is another hack and this one will quickly net you pretty large in memory tables and is perhaps reasonably good.
select POW(2,0)*t0.num + POW(2,1)*t1.num + POW(2,2)*t2.num + POW(2,3)*t3.num
as num
from (
select 0 as num UNION
select 1 as num
) t0, (
select 0 as num UNION
select 1 as num
) t1, (
select 0 as num UNION
select 1 as num
) t2, (
select 0 as num UNION
select 1 as num
) t3
order by num ;
+------+
| num |
+------+
| 0 |
| 1 |
....
| 14 |
| 15 |
+------+
16 rows in set (0.00 sec)
Will easily go to any power of 2 and should be fast enough.
with MS SQL you could use CTE :
;with numbs as
(
select 1 as col
union all
select col + 1 from numbs
where col < 10
)
select * from numbs
An SQL syntax that multiply records is CROSS JOIN
I am not sure from your request and comments, but I guess you are trying to do that.
I have done a similar thing to populate a table with a datestamp as string "YYYYMM" for a period of 20 years.
You did not specify the dbms you use, but with Oracle you could use CONNECT BY:
SELECT ROWNUM-1 i
FROM dual
CONNECT BY ROWNUM <= 10 + 1