How to declare a variable inside an Oracle select statement - sql

I have this Oracle SQL request:
SELECT col1,
col2,
DECODE(
SUM(CASE WHEN col3='A' AND col4='+' THEN col5 ELSE 0 END),
NULL,
0,
SUM(CASE WHEN col3='A' AND col4='+' THEN col5 ELSE 0 END)
)
FROM mytable
group by col1, col2;
I am asking if there is a way to declare a kind of variable and have something like this:
SELECT col1,
col2,
DECODE(
myVariable,
NULL,
0,
myVariable
)
FROM mytable
group by col1, col2;

no, but you could do a subquery:
SELECT col1,
col2,
DECODE(
SUM(myColumn),
NULL,
0,
SUM(myColumn)
)
FROM (
SELECT
col1,
col2,
CASE WHEN col3='A' AND col4='+' THEN col5 ELSE 0 END myColumn
FROM mytable
) a
group by col1, col2;

You can simlpy use coalesce() (or nvl()) instead of decode().
SELECT col1,
col2,
coalesce(sum(CASE
WHEN col3 = 'A'
AND col4 = '+' THEN
col5
ELSE
0
END),
0)
FROM mytable
GROUP BY col1,
col2;

You can use coalesce(). I think this is sufficient:
select col1, col2,
coalesce(sum(case when col3 = 'A' and col4 = '+' then col5 end), 0)
from mytable
group by col1, col2;
In actual fact, this expression:
sum(case when col3 = 'A' and col4 = '+' then col5 else 0 end)
Cannot return NULL in a query with a group by -- every group has at least one row and the else guarantees a 0 returns rather than NULL.
So, this should also do what you want:
select col1, col2,
sum(case when col3 = 'A' and col4 = '+' then col5 end)
from mytable
group by col1, col2;

Yes you can use substitution variables:
SELECT col1,
col2,
DECODE(
&&myVariable,
NULL,
0,
&&myVariable
)
FROM mytable
group by col1, col2;
More info here Oracle SQL*Plus Substitution Variables

Related

combine distinct row values into a string - sql

I would like to take cells in every row and make them into a string of names... My method already deals with casing.
For example, the table;
'john' | | 'smith' | 'smith'
'john' | 'paul' | | 'smith'
'john' | 'john' | 'john' |
returns:
'john smith'
'john paul smith'
'john'
This would need to run postgreSQL 8.2.15 of postgres so I can't make use of potentially useful functions like CONCAT, and data is in a greenplum db.
Alternatively, a method to directly delete duplicate tokens in a list of strings would let me achieve the larger objective. For example:
'john smith john smith'
'john john smith'
'smith john smith'
returns
'john smith'
'john smith'
'smith john'
The order of the tokens is not important, as long as all the unique values are returned, once only.
Thanks
Normalize your table structure, select distinct name values from that table, create a function to aggregate strings (see, e.g., How to concatenate strings of a string field in a PostgreSQL 'group by' query?), and apply that function. Except for the aggregate function creation, this could all be done in a single statement or view.
I've come up with a solution for you! :)
The following query returns the four columns (which I named col_1,2,3and 4) and removes the duplicates by joining the test_table with itself.
Here is the code:
SELECT t1.col_1, t2.col_2, t3.col_3, t4.col_4
FROM (
SELECT id, col_1
FROM test_table
) AS t1
LEFT JOIN (
SELECT id, col_2
FROM test_table
) as t2
ON (t2.id = t1.id and t2.col_2 <> t1.col_1)
LEFT JOIN (
SELECT id, col_3
FROM test_table
) as t3
ON (t3.id = t1.id and t3.col_3 <> t1.col_1 and t3.col_3 <> t2.col_2)
LEFT JOIN (
SELECT id, col_4
FROM test_table
) as t4
ON (t4.id = t1.id and t4.col_4 <> t1.col_1 and t4.col_4 <> t2.col_2 and t4.col_4 <> t3.col_3);
If you want to obtain the final string, you just substitute the "SELECT" row with this one:
SELECT trim(both ' ' FROM (COALESCE(t1.col_1, '') || ' ' || COALESCE(t2.col_2, '') || ' ' || COALESCE(t3.col_3, '') || ' ' || COALESCE(t4.col_4, '')))
this should work with your version of postgres, according with the docs:
[for the trim and concatenation functions]
https://www.postgresql.org/docs/8.2/static/functions-string.html
//***************************************************
[for the coalesce function]
https://www.postgresql.org/docs/8.2/static/functions-conditional.html
Please let me know if I've been of help :)
P.S. Your question sounds like a bad database design: I would have those columns moved on a table in which you could do this operation by using a group by or something similar. Moreover I would do the string concatenation on a separate script.
But that's my way of doing :)
I would do this by unpivoting the data and then reaggregation:
select id, string_agg(distinct col)
from (select id, col1 from t union all
select id, col2 from t union all
select id, col3 from t union all
select id, col4 from t
) t
where col is not null
group by id;
This assumes that each row has an unique id.
You can also use a giant case:
select concat_ws(',',
col1,
(case when col2 <> col1 then col2 end),
(case when col3 <> col2 and col3 <> col1 then col3 end),
(case when col4 <> col3 and col4 <> col2 and col4 <> col1 then col4 end)
) as newcol
from t;
In ancient versions of Postgres, you can phrase this as:
select trim(leading ',' from
(coalesce(',' || col1, '') ||
(case when col2 <> col1 then ',' || col2 else '' end) ||
(case when col3 <> col2 and col3 <> col1 then ',' || col3 else '' end),
(case when col4 <> col3 and col4 <> col2 and col4 <> col1 then ',' || col4 else '' end)
)
) as newcol
from t;

Dynamic Row Data into Column

I have a column which has 100 rows of data. I need to get the top 4 but in instead of rows I need to convert it into columns. Like Col1, Col2, Col3 and Col4.
I have tried
SELECT
MAX (CASE
WHEN rss_name = 'BBC-Sports'
THEN rss_name
END) AS col1,
MAX (CASE
WHEN rss_name = 'Talk Sports'
THEN rss_name
END) AS col2,
MAX (CASE
WHEN rss_name = 'Sky Sports'
THEN rss_name
END) AS col3,
MAX (CASE
WHEN rss_name = 'Crick Info'
THEN rss_name
END) AS col4
FROM
RSS
but it only works with static values:
I need
Col1, Col2, Col3, Col4
Sports,Talk Sports,Sky Sports,Crick Info
but since this is not constant data it will change and the values in Col keep changing.
You could use a derived table to set your column order then use your conditional aggregation on that.
SELECT
MAX(CASE WHEN Col_Rn = 1 THEN Rss_Name END) AS Col1,
MAX(CASE WHEN Col_Rn = 2 THEN Rss_Name END) AS Col2,
MAX(CASE WHEN Col_Rn = 3 THEN Rss_Name END) AS Col3,
MAX(CASE WHEN Col_Rn = 4 THEN Rss_Name END) AS Col4
FROM (
SELECT Rss_Name,
Row_Number() OVER (ORDER BY Rss_Name) AS Col_Rn -- set your order here
FROM RSS
) t
You need to use Dynamic Pivot. But in your case besides you need an extra column for Column names in Pivot like COL_1, COL_2....
Schema: (From your Image. Its better if you provide this sample data in Text).
CREATE TABLE #TAB (Rss_Name VARCHAR(50))
INSERT INTO #TAB
SELECT 'Sports'
UNION ALL
SELECT 'Talk Sports'
UNION ALL
SELECT 'Sky Sports'
UNION ALL
SELECT 'Crick Info'
Now Prepare your dynamic query as below
DECLARE #SQL VARCHAR(MAX)='',#PVT_COL VARCHAR(MAX)='';
--Preparing Dynamic Column List
SELECT #PVT_COL =#PVT_COL
+ '[COL_'+CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS VARCHAR(4))+'],'
FROM #TAB
SELECT #PVT_COL = LEFT(#PVT_COL,LEN(#PVT_COL)-1)
SELECT #SQL =
'SELECT * FROM (
SELECT Rss_Name
,''COL_''+CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS VARCHAR(4)) AS COL_NME
FROM #TAB
)AS A
PIVOT
(
MAX(Rss_Name) FOR COL_NME IN ('+#PVT_COL+')
)PVT'
EXEC (#SQL)
Result:
+--------+-------------+------------+------------+
| COL_1 | COL_2 | COL_3 | COL_4 |
+--------+-------------+------------+------------+
| Sports | Talk Sports | Sky Sports | Crick Info |
+--------+-------------+------------+------------+

SQL Server : Reuse calculated variable in select clause

I have the following table structure:
col1 col2 col3 col4
-------------------------------
aK Mbcd ABc defgh
col2, col3 and col4 columns are of type varchar(100) and col1 has type varchar(500).
I need a select query to have the output as following
col1 col2 col3 col4
-------------------------------
aK,Mb cd,A Bc,d efgh
Logic is explained as mentioned below:
In the result, Col2, col3 and col4 can have maximum 4 characters but col1 can have more than 4 characters upto 100.
If any column has more characters, last 4 characters will be retained in the same column and other extra columns will be concatenated with previous column's value separated by comma , and the same rule will be applied on the concatenated values as well.
I've written the following T-SQL statement. It works fine for last two columns. But I want to use new calculated value of col3 to strip out extra characters after adding some from col4
SELECT
CASE
WHEN X.Col4Length > 4
THEN concat(X.col3, ',', substring(x.col4, 0, X.Col4Length - 3))
ELSE X.col3
END AS col3,
CASE
WHEN X.Col4Length > 4
THEN substring(x.col4, X.Col4Length - 3, x.Col4Length)
ELSE X.col4
END AS col4
FROM
(SELECT
Col1, Col2, Col3, Col4,
Len(Col1) AS Col1Length,
Len(Col2) AS Col2Length,
Len(Col3) AS Col3Length,
Len(Col4) AS Col4Length
FROM
mytable) X
My try with a simple sub-query
with t1 as (
select 'aK' col1, 'Mbcd' col2, 'ABc' col3, 'defgh' col4
---
SELECT LEFT(col, LEN(col) - 12) col1,
RIGHT(LEFT(col, LEN(col) - 8), 4) col2,
RIGHT(LEFT(col, LEN(col) - 4), 4) col3,
RIGHT(col, 4) AS col4
FROM
(
SELECT col1+','+col2+','+col3+','+col4 AS col
FROM t1
) t;
You want to reuse calculated variables
There are two set-based /inline / adhoc approaches (and many more ugly procedural):
CTEs to do this for the whole set in advance
CROSS APPLY for the same on row level
Try it like this (CTE approach)
DECLARE #tbl TABLE(col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100),col4 VARCHAR(100));
INSERT INTO #tbl VALUES
('aK','Mbcd','ABc','defgh')
,('123456','abc','3456','123456789');
WITH ResolveCol4 AS
(
SELECT *
,RIGHT(col4,4) AS Col4_resolved
,col3 + ',' + CASE WHEN LEN(col4)>4 THEN SUBSTRING(col4,1,LEN(col4)-4) ELSE '' END AS col3_New
FROM #tbl
)
,ResolveCol3 AS
(
SELECT *
,RIGHT(col3_New,4) AS Col3_resolved
,col2 + ',' + CASE WHEN LEN(col3_New)>4 THEN SUBSTRING(col3_New,1,LEN(col3_New)-4) ELSE '' END AS col2_New
FROM ResolveCol4
)
,ResolveCol2 AS
(
SELECT *
,RIGHT(col2_New,4) AS Col2_resolved
,col1 + ',' + CASE WHEN LEN(col2_New)>4 THEN SUBSTRING(col2_New,1,LEN(col2_New)-4) ELSE '' END AS col1_New
FROM ResolveCol3
)
SELECT col1_new,Col2_resolved,Col3_resolved,Col4_resolved
FROM ResolveCol2
The result
aK,Mb cd,A Bc,d efgh
123456,abc,34 56,1 2345 6789

Complex summing in SQL

I'm working with a relational database that uses SQL99.
I have a series of 10 columns, each of the 10 columns contain a number value.
I need to sum each column individually and then take those sums and add them all together to get an overall sum. Then I must divide the overall sum by 15.
I've tried every format I can think of and have yet to return any results. I have no idea what the syntax should look like.
SELECT SUM(col1), SUM(col2)..., SUM(col1 + col2 + col3 + col4...)/15
FROM TABLENAME
GROUP BY 1=1
select
sum(col1) as sum1,
sum(col2) as sum2,
sum(col3) as sum3,
sum(col4) as sum4,
sum(col5) as sum5,
sum(col6) as sum6,
sum(col7) as sum7,
sum(col8) as sum8,
sum(col9) as sum9,
sum(col10) as as sum10,
sum( col1 + col2 + col3 + col4 + col5 + col6 + col7 + col8 + col9 + col10) as overallsum,
sum( col1 + col2 + col3 + col4 + col5 + col6 + col7 + col8 + col9 + col10) / 15 as dividedsum
from
tablename
SELECT SUM(subsum) / 15 FROM (
SELECT SUM(column1) AS subsum
FROM table
UNION ALL
SELECT SUM(column2) AS subsum
FROM table
UNION ALL
...
SELECT SUM(column10) AS subsum
FROM table
)

how to modify this particular query?

SELECT * FROM foobar
WHERE userid != '100' AND col1 REGEXP '[[:<:]]test[[:>:]]'
OR userid != '100' AND col2 REGEXP '[[:<:]]test[[:>:]]'
OR userid != '100' AND col3 REGEXP '[[:<:]]test[[:>:]]'
This query is working fine for me. It will filter basically on two criteria
where col1 or col2 or col3 have "test" and
userid is not 100.
I have another col4 which I want that, other than the above two condition, it must filter out those results where col4 = 'y'
How should I modify this above query?
You have an error in your query, You need to change the second col2 to col3. It's easier to see the error if you reformat your code:
SELECT * FROM foobar
WHERE (userid != '100' AND col1 REGEXP '[[:<:]]test[[:>:]]') OR
(userid != '100' AND col2 REGEXP '[[:<:]]test[[:>:]]') OR
(userid != '100' AND col2 REGEXP '[[:<:]]test[[:>:]]')
I've also added parentheses to make the evaluation order clear. You can rewrite your query to avoid repeating the expression userid != '100'. Then just add your missing clause:
SELECT * FROM foobar
WHERE userid != '100'
AND col4 <> 'y'
AND (
col1 REGEXP '[[:<:]]test[[:>:]]' OR
col2 REGEXP '[[:<:]]test[[:>:]]' OR
col3 REGEXP '[[:<:]]test[[:>:]]'
)
Something like this? You could also separate out the userid != '100' since it is common to the three checks.
SELECT *
FROM foobar
WHERE userid != '100'
AND (col1 REGEXP '[[:<:]]test[[:>:]]'
OR col2 REGEXP '[[:<:]]test[[:>:]]'
OR col3 REGEXP '[[:<:]]test[[:>:]]' )
AND col4 != 'y'
SELECT *
FROM foobar
WHERE userid != '100'
AND (col1 REGEXP '[[:<:]]test[[:>:]]'
OR col2 REGEXP '[[:<:]]test[[:>:]]'
OR col3 REGEXP '[[:<:]]test[[:>:]]')
AND col4 <> 'y'
Try this:
SELECT *
FROM foobar
WHERE col4 = 'y'
OR (
userid != '100'
AND (
col1 REGEXP '[[:<:]]test[[:>:]]'
OR
col2 REGEXP '[[:<:]]test[[:>:]]'
OR
col2 REGEXP '[[:<:]]test[[:>:]]'
)
)
SELECT * FROM foobar WHERE
(userid != '100') AND
(col1 REGEXP '[[:<:]]test[[:>:]]' OR userid != '100' OR col2 REGEXP '[[:<:]]test[[:>:]]' OR col2 REGEXP '[[:<:]]test[[:>:]]')
AND col4 <> 'y'