How can I use a CASE in a SELECT in BigQuery if a field exactly matches one or more text strings (words)? - select

What I want to do is to do something (THEN) when another field contains either 'Click video' or 'Click button'
SELECT
CASE
WHEN field IS EXACTLY 'Click video' or 'Click button' THEN do something to another field
END

The simpler approach I see
SELECT
CASE
WHEN field in ('Click video', 'Click button')
THEN do something to another field
END

Try IF or CASE:
WITH Numbers AS
(SELECT 10 as A, 20 as B UNION ALL
SELECT 50, 30 UNION ALL
SELECT 60, 60)
SELECT
A, B,
IF( A<B, 'true', 'false') as result
FROM Numbers
WITH Numbers AS
(SELECT 90 as A, 2 as B UNION ALL
SELECT 50, 8 UNION ALL
SELECT 60, 6 UNION ALL
SELECT 50, 10)
SELECT A, B,
CASE A
WHEN 90 THEN 'red'
WHEN 50 THEN 'blue'
ELSE 'green'
END
AS result
FROM Numbers
WITH Numbers AS
(SELECT 90 as A, 2 as B UNION ALL
SELECT 50, 6 UNION ALL
SELECT 20, 10)
SELECT A, B,
CASE
WHEN A > 60 THEN 'red'
WHEN A > 30 THEN 'blue'
ELSE 'green'
END
AS result
FROM Numbers

Related

Select top 5 and bottom 5 columns from a list of columns based on their values

I have a requirement where I need to Select top 5 and bottom 5 columns from a list of columns based on their values.
If more than 1 column has same value then select any one from them.
Eg
CREATE TABLE #b(Company VARCHAR(10),A1 INt,A2 INt,A3 INt,A4 INt,B1 INt,G1 INt,G2 INt,G3 INt,HH5 INt,SS6 INt)
INSERT INTo #b
SELECT 'test_A',8,10,6,10,0,6,0,6,13,4 UNION ALL
SELECT 'test_B',17,7,0,1,3,18,0,6,9,5 UNION ALL
SELECT 'test_C',0,0,6,1,2,6,3,4,3,2 UNION ALL
SELECt 'test_D',13,1,4,1,4,1,9,0,0,5
SELECT * FROM #b
Desired Output:
Company
Top5
Bottom5
test_A
HH5,A2,A1,A3,SS6
B1,SS6,A3,A1,A2
test_B
G1,A1,HH5,A2,G3
A3,A4,B1,SS6,G3
I am able to find the top values but not the column names.
Here is I am stuck at, I am able to find the max scores but not sure how to find the column that holds this max value.
SELECT Company,(
SELECT MAX(myval)
FROM (VALUES (A1),(A2),(A3),(A4),(B1),(G1),(G2),(G3),(HH5)) AS temp(myval))
AS MaxOfColumns
FROM #b
As Larnu suggested, the first step would be to UNPIVOT the data into a form like (Company, ColumnName, Value). You can then use the ROW_NUMBER() window function to assign ordinals 1 - 10 to each value for each company based on the sorted value.
Next, you can wrap the above in a Common Table Expression (CTE) to feed a query that, for each Company, uses conditional aggregation with the STRING_AGG() to selectively combine the top 5 and bottom 5 column names to produce the desired result.
Something like:
;WITH Data AS (
SELECT
Company,
ColumnName,
Value,
ROW_NUMBER() OVER(PARTITION BY Company ORDER BY Value DESC, ColumnName) AS Ord
FROM #b
UNPIVOT (
Value FOR ColumnName IN (A1, A2, A3, A4, B1, G1, G2, G3, HH5, SS6)
) U
)
SELECT
D.Company,
STRING_AGG(CASE WHEN D.Ord BETWEEN 1 AND 5 THEN D.ColumnName END, ', ')
WITHIN GROUP (ORDER BY D.ORD) AS Top5,
STRING_AGG(CASE WHEN D.Ord BETWEEN 6 AND 10 THEN D.ColumnName END, ', ')
WITHIN GROUP (ORDER BY D.ORD) AS Bottom5
FROM Data D
GROUP BY D.Company
ORDER BY D.Company
For older SQL Server versions that don't support STRING_AGG(), the FOR XML PATH(''),TYPE construct can be used to concatenate text. The .value('text()[1]', 'varchar(max)') function is then used to safely extract the result from the XML, and finally the STUFF() function is used to strip out the leading separator (comma-space).
;WITH Data AS (
SELECT
Company,
ColumnName,
Value,
ROW_NUMBER() OVER(PARTITION BY Company ORDER BY Value DESC, ColumnName) AS Ord
FROM #b
UNPIVOT (
Value FOR ColumnName IN (A1, A2, A3, A4, B1, G1, G2, G3, HH5, SS6)
) U
)
SELECT B.Company, C.Top5, C.Bottom5
FROM #b B
CROSS APPLY (
SELECT
STUFF((
SELECT ', ' + D.ColumnName
FROM Data D
WHERE D.Company = B.Company
AND D.Ord BETWEEN 1 AND 5
ORDER BY D.ORD
FOR XML PATH(''),TYPE
).value('text()[1]', 'varchar(max)'), 1, 2, '') AS Top5,
STUFF((
SELECT ', ' + D.ColumnName
FROM Data D
WHERE D.Company = B.Company
AND D.Ord BETWEEN 6 AND 10
ORDER BY D.ORD
FOR XML PATH(''),TYPE
).value('text()[1]', 'varchar(max)'), 1, 2, '') AS Bottom5
) C
ORDER BY B.Company
See this db<>fiddle fr a demo.
If you also want lists of the top 5 and bottom 5 values, you can repeat the aggregations above while substituting CONVERT(VARCHAR, D.Value) for D.ColumnName where appropriate.

Select statement based on category

I have a table like this
Code
A123
B3123
C93485
D345
E29845
The first letter of rows in code column are classified into the following:
Char Category
A-B A
C B
D-E C
I would like to display the output table like this
Category Total Percentage
A 2 0.4%
B 1 0.2%
C 2 0.4%
Total 5 1.0%
I'm not sure how to start. Any hints or help is much appreciated
Here is one option:
SELECT
CASE WHEN SUBSTR(Code, 1, 1) IN ('A', 'B') THEN 'A'
WHEN SUBSTR(Code, 1, 1) = 'C' THEN 'B'
ELSE 'C' END AS Category,
COUNT(*) AS Total,
200.0 * COUNT(*) / SUM(COUNT(*)) OVER () AS Percentage
FROM yourTable
GROUP BY
ROLLUP(CASE WHEN SUBSTR(Code, 1, 1) IN ('A', 'B') THEN 'A'
WHEN SUBSTR(Code, 1, 1) = 'C' THEN 'B'
ELSE 'C' END);
Demo
This approach uses a CASE expression on the first letter of each code to assign a category. Then, we aggregate by category and find the totals, as well as the percentages. Note that ROLLUP is used to generate a total record at the bottom of the result set. As a side effect of this, we multiply by 200%, because the summary row already contains the entire table count, which then gets counted twice.

How to add a dash between running numbers and comma between non-running numbers

I would like to replace a set of running and non running numbers with commas and hyphens where appropriate.
Using STUFF & XML PATH I was able to accomplish some of what I want by getting something like 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 15, 19, 20, 21, 22, 24.
WITH CTE AS (
SELECT DISTINCT t1.ORDERNo, t1.Part, t2.LineNum
FROM [DBName].[DBA].Table1 t1
JOIN Table2 t2 ON t2.Part = t1.Part
WHERE t1.ORDERNo = 'AB12345')
SELECT c1.ORDERNo, c1.Part, STUFF((SELECT ', ' + CAST(LineNum AS VARCHAR(5))
FROM CTE c2
WHERE c2.ORDERNo= c1.ORDERNo
FOR XML PATH('')), 1, 2, '') AS [LineNums]
FROM CTE c1
GROUP BY c1.ORDERNo, c1.Part
Here is some sample output:
ORDERNo Part LineNums
ON5650 PT01-0181 5, 6, 7, 8, 12
ON5652 PT01-0181 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 15, 19, 20, 21, 22, 24
ON5654 PT01-0181 1, 4
ON5656 PT01-0181 1, 2, 4
ON5730 PT01-0181 1, 2
ON5253 PT16-3934 1, 2, 3, 4, 5
ON1723 PT02-0585 1, 2, 3, 6, 8, 9, 10
Would like to have:
OrderNo Part LineNums
ON5650 PT01-0181 5-8, 12
ON5652 PT01-0181 1-10, 13, 15, 19-22, 24
ON5654 PT01-0181 1, 4
ON5656 PT01-0181 1-2, 4
ON5730 PT01-0181 1-2
ON5253 PT16-3934 1-5
ON1723 PT02-0585 1-3, 6, 8-10
This is a classic gaps-and-islands problem.
(a good read on the subject is Itzik Ben-Gan's Gaps and islands from SQL Server MVP Deep Dives)
The idea is that you first need to identify the groups of consecutive numbers. Once you've done that, the rest is easy.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
N int
);
INSERT INTO #T VALUES
(1), (2), (3), (4),
(6),
(8),
(10), (11),
(13), (14), (15),
(17),
(19), (20), (21),
(25);
Then, use a common table expression to identify the groups.
With Grouped AS
(
SELECT N,
N - ROW_NUMBER() OVER(ORDER BY N) As Grp
FROM #T
)
The result if this cte is this:
N Grp
1 0
2 0
3 0
4 0
6 1
8 2
10 3
11 3
13 4
14 4
15 4
17 5
19 6
20 6
21 6
25 9
As you can see, while the numbers are consecutive, the grp value stays the same.
When a row has a number that isn't consecutive with the previous number, the grp value changes.
Then you select from that cte, using a case expression to either select a single number (if it's the only one in it's group) or the start and end of the group, separated by a dash:
SELECT STUFF(
(
SELECT ', ' +
CASE WHEN MIN(N) = MAX(N) THEN CAST(MIN(N) as varchar(11))
ELSE CAST(MIN(N) as varchar(11)) +'-' + CAST(MAX(N) as varchar(11))
END
FROM Grouped
GROUP BY grp
FOR XML PATH('')
), 1, 2, '') As GapsAndIslands
The result:
GapsAndIslands
1-4, 6, 8, 10-11, 13-15, 17, 19-21, 25
For fun I put together another way using Window Aggregates (e.g. SUM() OVER ...). I also use some newer T-SQL functionality such as CONCAT (2012+) and STRING_AGG (2017+). This using Zohar's sample data.
DECLARE #T AS TABLE(N INT PRIMARY KEY CLUSTERED);
INSERT INTO #T VALUES (1),(2),(3),(4),(6),(8),(10),(11),(13),(14),(15),(17),(19),(20),(21),(25);
WITH
a AS (
SELECT t.N,isNewGroup = SIGN(t.N-LAG(t.N,1,t.N-1) OVER (ORDER BY t.N)-1)
FROM #t AS t),
b AS (
SELECT a.N, GroupNbr = SUM(a.isNewGroup) OVER (ORDER BY a.N)
FROM a),
c AS (
SELECT b.GroupNbr,
txt = CONCAT(MIN(b.N), REPLICATE(CONCAT('-',MAX(b.N)), SIGN(MAX(b.N)-MIN(b.N))))
FROM b
GROUP BY b.GroupNbr)
SELECT STRING_AGG(c.txt,', ') WITHIN GROUP (ORDER BY c.GroupNbr) AS Islands
FROM c;
Returns:
Islands
1-4, 6 , 8, 10-11, 13-15, 17, 19-21, 25
And here an approach using a recursive CTE.
DECLARE #T AS TABLE(N INT PRIMARY KEY CLUSTERED);
INSERT INTO #T VALUES (1),(2),(3),(4),(6),(8),(10),(11),(13),(14),(15),(17),(19),(20),(21),(25);
WITH Numbered AS
(
SELECT N, ROW_NUMBER() OVER(ORDER BY N) AS RowIndex FROM #T
)
,recCTE AS
(
SELECT N
,RowIndex
,CAST(N AS VARCHAR(MAX)) AS OutputString
,(SELECT MAX(n2.RowIndex) FROM Numbered n2) AS MaxRowIndex
FROM Numbered WHERE RowIndex=1
UNION ALL
SELECT n.N
,n.RowIndex
,CASE WHEN A.TheEnd =1 THEN CONCAT(r.OutputString,CASE WHEN IsIsland=1 THEN '-' ELSE ',' END, n.N)
WHEN A.IsIsland=1 AND A.IsWithin=0 THEN CONCAT(r.OutputString,'-')
WHEN A.IsIsland=1 AND A.IsWithin=1 THEN r.OutputString
WHEN A.IsIsland=0 AND A.IsWithin=1 THEN CONCAT(r.OutputString,r.N,',',n.N)
ELSE CONCAT(r.OutputString,',',n.N)
END
,r.MaxRowIndex
FROM Numbered n
INNER JOIN recCTE r ON n.RowIndex=r.RowIndex+1
CROSS APPLY(SELECT CASE WHEN n.N-r.N=1 THEN 1 ELSE 0 END AS IsIsland
,CASE WHEN RIGHT(r.OutputString,1)='-' THEN 1 ELSE 0 END AS IsWithin
,CASE WHEN n.RowIndex=r.MaxRowIndex THEN 1 ELSE 0 END AS TheEnd) A
)
SELECT TOP 1 OutputString FROM recCTE ORDER BY RowIndex DESC;
The idea in short:
First we create a numbered set.
The recursive CTE will use the row's index to pick the next row, thus iterating through the set row-by-row
The APPLY determines three BIT values:
Is the distance to the previous value 1, then we are on the island, otherwise not
Is the last character of the growing output string a hyphen, then we are waiting for the end of an island, otherwise not.
...and if we've reached the end
The CASE deals with this four-field-matrix:
First we deal with the end to avoid a trailing hyphen at the end
Reaching an island we add a hyphen
Staying on the island we just continue
Reaching the end of an island we add the last number, a comma and start a new island
any other case will just add a comma and start a new island.
Hint: You can read island as group or section, while the commas mark the gaps.
Combining what I already had and using Zohar Peled's code I was finally able to figure out a solution:
WITH cteLineNums AS (
SELECT TOP 100 PERCENT t1.OrderNo, t1.Part, t2.LineNum
, (t2.line_number - ROW_NUMBER() OVER(PARTITION BY t1.OrderNo, t1.Part ORDER BY t1.OrderNo, t1.Part, t2.LineNum)) AS RowSeq
FROM [DBName].[DBA].Table1 t1
JOIN Table2 t2 ON t2.Part = t1.Part
WHERE t1.OrderNo = 'AB12345')
GROUP BY t1.OrderNo, t1.Part, t2.LineNum
ORDER BY t1.OrderNo, t1.Part, t2.LineNum)
SELECT OrderNo, Part
, STUFF((SELECT ', ' +
CASE WHEN MIN(line_number) = MAX(line_number) THEN CAST(MIN(line_number) AS VARCHAR(3))
WHEN MIN(line_number) = (MAX(line_number)-1) THEN CAST(MIN(line_number) AS VARCHAR(3)) + ', ' + CAST(MAX(line_number) AS VARCHAR(3))
ELSE CAST(MIN(line_number) AS VARCHAR(3)) + '-' + CAST(MAX(line_number) AS VARCHAR(3))
END
FROM cteLineNums c1
WHERE c1.OrderNo = c2.OrderNo
AND c1.Part = c2.Part
GROUP BY OrderNo, Part
ORDER BY OrderNo, Part
FOR XML PATH('')), 1, 2, '') AS [LineNums]
FROM cteLineNums c2
GROUP BY OrderNo, Part
I used the ROW_NUMBER() OVER PARTITION BY since I returned multiple records with different Order Numbers and Part Numbers. All this lead to me still having to do the self join in the second part in order to get the correct LineNums to show for each record.
The second WHEN in the CASE statement is due to the code defaulting to having something like 2, 5, 8-9, 14 displayed when it should be 2, 5, 8, 9, 14.

T-SQL query with aggregate function and CASE-WHEN statement

I have a simple but very important concept to clear in T-SQL.
I am writing a lot of T-SQL queries against a table, with a lot of aggregations and GROUP BY.
Now, in the SELECT clause of my T-SQL query, I have a CASE-WHEN statements. Please see below:
Statement 1:
SELECT X, Y, Z,
A = CASE
WHEN P = 1 THEN B
ELSE Q
END,
SUM(Sales)
FROM mytable
GROUP BY
X, Y, Z,
CASE
WHEN P = 1 THEN B
ELSE Q
END
Now can Statement 1 be written as Statement 2 ?
Statement 2:
SELECT X, Y, Z,
A = CASE
WHEN P = 1 THEN B
ELSE Q
END,
SUM(Sales)
FROM mytable
GROUP BY
X, Y, Z,
P, B, Q
Is Statement 1 = Statement 2 ?
Can the CASE-WHEN in the SELECT clause be modified in the GROUP BY clause into individual columns?
Will the result set be the same always ?
The difference relies on the amount of different values you might get from columns P, B and Q, against the result of your CASE statement. You can spot the different on this example.
IF OBJECT_ID('tempdb..#Data') IS NOT NULL
DROP TABLE #Data
CREATE TABLE #Data (
P INT,
B INT,
Q INT,
Sales INT)
INSERT INTO #Data (
P,
B,
Q,
Sales)
VALUES
(1, 20, 300, 1000),
(1, 20, 400, 500),
(2, 1, 1, 50),
(2, 1, 1, 250)
-- Statement 2
SELECT
P,
B,
Q,
TotalSales = SUM(D.Sales)
FROM
#Data AS D
GROUP BY
P,
B,
Q
/*
All different combinations of PBQ and listed, and their sales added
P B Q TotalSales
1 20 300 1000
1 20 400 500
2 1 1 300
*/
-- Statement 1
SELECT
CaseResult = CASE WHEN P = 1 THEN B ELSE Q END,
TotalSales = SUM(D.Sales)
FROM
#Data AS D
GROUP BY
CASE WHEN P = 1 THEN B ELSE Q END
/*
The grouping value depends on value B when P = 1 (and not on Q!) so
all records with P = 1 and same B are grouped together and
all records with P = 0 and same Q are grouped together
CaseResult TotalSales
1 300
20 1500
*/
There might be the case when you data doesn't generate different values from the CASE to the combination of P, B and Q, in that case the results will be the same for both queries.

How to do grand total on level break footer total in Report Design Aid

I want to calculate a grand total of those total I get on level break footer group.
Example:
Item A1 - 10
Item A2 - 20
Item A3 - 30
Total = 60
Item B1 - 10
Item B2 - 20
Total = 30
Grand Total 90
What should I do?
You can use rollup. Hope that helps.
EDIT: Did not recognized that the question is related to JDEdwards so this is how it would be done in Oralce SQL.
with
items as (
select 'A' as grp, '1' as item, 10 as val from dual
union
select 'A' as grp, '2' as item, 20 as val from dual
union
select 'A' as grp, '3' as item, 30 as val from dual
union
select 'B' as grp, '1' as item, 10 as val from dual
union
select 'B' as grp, '2' as item, 20 as val from dual
)
select case
when grouping(grp) = 1 then 'Grand Total ' || grp || sum(val)
when grouping(item) = 1 then 'Total ' || grp || ' = ' || sum(val)
else 'Item ' || grp || item || ' - ' || max(val)
end
from items
group by rollup (grp, item)
You should set which fields of your Business View you will sequence. For instance, Branch/Plant(MCU) / Date Transaction(TRDJ) / Item (LITM)
Define which of these fields you want a Level Break.
Then create a Level Break Footer Group Section based on the field you defined on the previous step and add an aggregation function to sum the amount fields you want.
I assume you have more than 2 sort columns.
Let us assume them to be MCU(Business Unit),DOCO(Order No),LITM(Item Number)
You will get your subtotal by putting a level-break footer on DOCO and using the aggregate function (sumof) on BC LITM (The item needs to be displayed in the parent section)
To get Grand Total, you can do 2 things:
a: Make a global variable and store the subtotal in it and keep adding it to itself. (Display it at the end as Grand Total)
b: Add a level-break footer (on MCU) inside the existing level-break footer and perform sumof on BC LITM
I hope this solves your problem :)