Show Zero if there is no record count - ORACLE SQL query - sql

The below Oracle query gives if there are any different errors with error_message and Serial_num.
If there is ZERO or No Different error count instead of showing Blank/Null result. How can i see the output like this? I tried with NVL(error_message,0) and COALESCE (Sum(total),0) but not getting the desired output.
Expected output:
1 Different Errors: 0
Oracle SQL Query:
SELECT
1 as Index_Num,
CONCAT('Different Errors: ', error_message || '# ' || serial_num),
SUM(total)
FROM (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
)
GROUP BY error_message, serial_num

Create a CTE for the subquery and use UNION ALL with NOT EXISTS to cover the case that the CTE does not return any rows:
WITH cte AS (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
)
SELECT
1 as Index_Num,
CONCAT(
'Different Errors: ',
list_agg(error_message || '# ' || serial_num) within group (order by error_message)
),
SUM(total)
FROM cte
UNION ALL
SELECT 1, 'Different Errors: ', 0
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM cte)

D'oh! Looks like I took too long. Here's another option for posterity:
SELECT
1,
CONCAT(
'Different Errors: ',
CASE
WHEN src.error_message IS NULL THEN ''
ELSE src.error_message || ' # ' || src.serial_num
END
) Summary,
COALESCE(src.total, 0) AS total
FROM dual -- Get a seed row (in case there are no rows in error table)
LEFT JOIN (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
) src ON 0=0
SQL Fiddle

It is not exactly what you are asking for, but might prove useful. You can easily add a row with the total number of errors, using grouping sets:
SELECT 1 as Index_Num,
('Different Errors: ' || error_message || '# ' || serial_num),
COUNT(*) as total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY GROUPING SETS ( (error_message, serial_num), () );
Alas, this produces the summary row even when there are errors. It occurs to me that you might find this useful.

Related

In BigQuery, identify when columns do not match on UNION ALL

with
table1 as (
select 'joe' as name, 17 as age, 25 as speed
),
table2 as (
select 'nick' as name, 21 as speed, 23 as strength
)
select * from table1
union all
select * from table2
In Google BigQuery, this union all does not throw an error because both tables have the same number of columns (3 each). However I receive bad data output because the columns do not match. Rather than outputting a new table with 4 columns name, age, speed, strength with correct values + nulls for missing values (which would probably be preferred), the union all keeps the 3 columns from the top row.
Is there a good way to catch that the columns do not match, rather than the query silently returning bad data? Is there any way for this to return an error perhaps, as opposed to a successful table? I'm not sure how to check in SQL that the columns in the 2 tables match.
Edit: in this example it is clear to see that the columns do not match, however in our data we have 100+ columns and we want to avoid a situation where we make an error in a UNION ALL
Below is for BigQuery Standard SQL and using scripting feature of BQ
DECLARE statement STRING;
SET statement = (
WITH table1_columns AS (
SELECT column FROM (SELECT * FROM `project.dataset.table1` LIMIT 1) t,
UNNEST(REGEXP_EXTRACT_ALL(TRIM(TO_JSON_STRING(t), '{}'), r'"([^"]*)":')) column
), table2_columns AS (
SELECT column FROM (SELECT * FROM `project.dataset.table2` LIMIT 1) t,
UNNEST(REGEXP_EXTRACT_ALL(TRIM(TO_JSON_STRING(t), '{}'), r'"([^"]*)":')) column
), all_columns AS (
SELECT column FROM table1_columns UNION DISTINCT SELECT column FROM table2_columns
)
SELECT (
SELECT 'SELECT ' || STRING_AGG(IF(t.column IS NULL, 'NULL as ', '') || a.column, ', ') || ' FROM `project.dataset.table1` UNION ALL '
FROM all_columns a LEFT JOIN table1_columns t USING(column)
) || (
SELECT 'SELECT ' || STRING_AGG(IF(t.column IS NULL, 'NULL as ', '') || a.column, ', ') || ' FROM `project.dataset.table2`'
FROM all_columns a LEFT JOIN table2_columns t USING(column)
)
);
EXECUTE IMMEDIATE statement;
when applied to sample data from your question - output is
Row name age speed strength
1 joe 17 25 null
2 nick null 21 23
After saving table1 and table2 as 2 tables in a dataset in BigQuery, I then used the metadata using INFORMATION_SCHEMA to check that the columns matched.
SELECT *
FROM models.INFORMATION_SCHEMA.COLUMNS
where table_name = 'table1'
SELECT *
FROM models.INFORMATION_SCHEMA.COLUMNS
where table_name = 'table2'
INFORMATION_SCHEMA.COLUMNS returns information including the column names and their positioning. I can join these 2 tables together then to check that the names match...

How to avoid duplicates in the STRING_AGG function SQL Server

I was testing a query in SQL in which I need to concatenate values ​​in the form of a comma-separated list, and it works, I just have the problem of duplicate values.
This is the query:
SELECT t0.id_marcas AS CodMarca,
t0.nombremarcas AS NombreMarca,
t0.imagenmarcas,
(SELECT String_agg((t2.name), ', ')
FROM exlcartu_devcit.store_to_cuisine t1
INNER JOIN exlcartu_devcit.cuisine t2
ON t1.cuisine_id = t2.cuisine_id
WHERE store_id = (SELECT TOP 1 store_id
FROM exlcartu_devcit.store
WHERE id_marcas = t0.id_marcas
AND status = 1)) AS Descripcion,
t0.logo,
t0.imagen,
(SELECT TOP 1 preparing_time
FROM exlcartu_devcit.store
WHERE id_marcas = t0.id_marcas
AND status = 1) AS Tiempo,
t0.orden,
(SELECT TOP 1 Avg(minimum_amount)
FROM exlcartu_devcit.store_delivery_zone
WHERE id_marcas = t0.id_marcas) AS MontoMinimo
FROM exlcartu_devcit.[marcas] t0
I thought the solution could be just adding a DISTINCT to the query to avoid repeated values ​​in this way ...
(SELECT STRING_AGG(DISTINCT (t2.name), ', ') AS Descripcion
But apparently the STRING_AGG() function does not support it, any idea how to avoid repeated values?
Simplest way is just select from select, like this:
with dups as (select 1 as one union all select 1 as one)
select string_agg(one, ', ') from (select distinct one from dups) q;
vs original
with dups as (select 1 as one union all select 1 as one)
select string_agg(one, ', ') from dups;

Update count of union

I have this query which is throwing a compilation error at the last ')'. The intellisense says 'Expected AS, ID or QUOTED_ID'.
What I am trying to do is - find the distinct values from the union of a table select and a function select, then get the count and update the column of another table with that value.
UPDATE #referees
SET [TotalKeywordCount] = (select count(*)
from (select Keyword
from [dbo].[RefereeFinderPersonKeyWord] P
where P.p_id=#referees.p_id
union
SELECT ltrim(rtrim(replace(Data, '''', '')))
from [SplitOne] (#keywords, ',')))
Any idea what I am doing wrong?
You need to add a name to the nested query that you use in the FROM of the query that pulls out the value for [TotalKeywordCount]. Below you have the code that assigns to it the name subquery:
UPDATE #referees
SET [TotalKeywordCount] = (select count(*) from (
select Keyword from [dbo].[RefereeFinderPersonKeyWord] P where P.p_id=#referees.p_id
union
SELECT ltrim(rtrim(replace(Data, '''', ''))) from [SplitOne] (#keywords, ',')) subquery )

How to use ROWNUM with nested queries

SELECT * FROM(
SELECT * FROM(
SELECT PART_NO, SRC_PART_NO, CTNM_ENG, DESCRIPTION, USER_ID, REG_DT, CHG_DT, FLAG,
(select count(*) from ( SELECT PART_NO, SRC_PART_NO, CTNM_ENG, DESCRIPTION, USER_ID, REG_DT, CHG_DT, FLAG
FROM GM_PART_LIST
WHERE PART_NO LIKE '%' || '%' AND SRC_PART_NO LIKE '%' || '%' AND CTNM_ENG LIKE 'BOLT'|| '%'
AND 1 = 1) ) as total_count -- Nested subquery that return total count of record set. plug in same where conditions.
FROM GM_PART_LIST
WHERE PART_NO LIKE '%' || '%' AND SRC_PART_NO LIKE '%' || '%' AND CTNM_ENG LIKE 'BOLT'|| '%'
AND 1 = 1
ORDER BY PART_NO ASC))
WHERE ROWNUM BETWEEN 2 AND 202;
How is it that with the above query if I search between 1 and 200 It pulls records fine but when I switch it to 2 or another integer above 1 it fails to query any records?
is this a syntax issue? thank you in advance for any help anyone can offer.
ROWNUM is assigned when a row is evaluated for the where conditions. The first row from the row source is retrieved and given ROWNUM=1. If one of the were conditions is ROWNUM > 1, this row will not be selected.
Then ROWNUM=1 is reassigned to the next row (which again will fail the where clause) and so on. This is because in the end ROWNUM must run consecutively from 1, it will not be a sequence with gaps. So any condition that doesn't allow ROWNUM to be 1 (example: where mod(ROWNUM, 2) = 0) will produce zero rows, and for exactly the same reason.

linking the query in the select statement

From the query i am able to return the output as expected ..
Is it possible to write the same query in the below marked quotes using select statement:
SELECT ACC.ACCOUNT_NUM AS ACCOUNT_NUMBER,
ACC.ACCOUNT_NAME AS ACCOUNT_NAME,
ADR.ADDRESS_1 AS BUILDING_TYPE,
ACC.CUSTOMER_REF AS CUSTOMER_ID,
CAT.BC AS BILL_CYCLE,
CES.EVENT_SOURCE AS TELEPHONE_NUMBER,
AAT.PACKAGE_NAME AS PROMO_PACKAGE,
PRD.PRODUCT_NAME AS SERVICES,
(SELECT COUNT (DISTINCT (EVENT_SOURCE))
FROM CUSTEVENTSOURCE CES1
WHERE CES1.CUSTOMER_REF = ACC.CUSTOMER_REF AND END_DTM IS NULL)
AS TOTAL_NUMBER,
("SELECT LISTAGG (EVENT_SOURCE, ', ')
WITHIN GROUP (ORDER BY EVENT_SOURCE)
FROM CUSTEVENTSOURCE
WHERE CUSTOMER_REF = ACC.CUSTOMER_REF AND END_DTM IS NULL")
AS ALL_IPHONE_NUMBERS,
CUS.COMPANY_NAME AS COMPANY_NAME,
ADR.ADDRESS_1
|| ' '
|| ADR.ADDRESS_2
|| ' '
|| ADR.ADDRESS_3
|| ' '
|| ADR.ADDRESS_4
|| ' '
|| ADR.ADDRESS_5
AS BILLING_ADDRESS,
(SELECT ADR.ADDRESS_1
FROM ADDRESS ADR
WHERE ADR.CUSTOMER_REF = ACC.CUSTOMER_REF
AND ADDRESS_SEQ IN
(SELECT ADDRESS_SEQ
FROM CUSTPRODUCTADDRESS CPA
WHERE CPA.CUSTOMER_REF = ADR.CUSTOMER_REF
AND PRODUCT_SEQ IN
(SELECT PRODUCT_SEQ
FROM CUSTPRODUCTSTATUS CPS
WHERE CPS.CUSTOMER_REF =
CPA.CUSTOMER_REF
AND EFFECTIVE_DTM =
(SELECT MAX (
EFFECTIVE_DTM)
FROM CUSTPRODUCTSTATUS CPS1
WHERE CPS1.CUSTOMER_REF =
CPS.
CUSTOMER_REF))))
AS INSTALLATION_ADDRESS,
(SELECT ACS.EFFECTIVE_DTM
FROM ACCOUNTSTATUS ACS
WHERE ACS.ACCOUNT_NUM = ACC.ACCOUNT_NUM
AND ACS.EFFECTIVE_DTM =
(SELECT MAX (EFFECTIVE_DTM)
FROM ACCOUNTSTATUS ACS1
WHERE ACS1.ACCOUNT_NUM = ACS.ACCOUNT_NUM
AND ACCOUNT_STATUS = 'OK'))
AS ACTIVATION_DATE,
(SELECT ACS.ACCOUNT_STATUS
FROM ACCOUNTSTATUS ACS
WHERE ACS.ACCOUNT_NUM = ACC.ACCOUNT_NUM
AND ACS.EFFECTIVE_DTM =
(SELECT MAX (EFFECTIVE_DTM)
FROM ACCOUNTSTATUS ACS1
WHERE ACS1.ACCOUNT_NUM = ACS.ACCOUNT_NUM))
AS ACCOUNT_STATUS,
(SELECT ACS.STATUS_REASON_TXT
FROM ACCOUNTSTATUS ACS
can u please help me out..I am new to sql..please
Thanks in advance..
The above needs to be changed within the "" double quotes
The output looks like:
ACCOUNT_NUMBER ACCOUNT_NAME BUILDING_TYPE CUSTOMER_ID TELEPHONE_NUMBER PROMO_PACKAGE TOTAL_NUMBER ALL_IPHONE_NUMBERS,
MTX000110 John xxxx 10002 123456 yyy 3 001-003,004,007
If the account has 3 totoal_number then in the All_iphone_numbers column if the values have sequenced numbers then it should '-' with separator otherwise it has to display the number
If I use the " " doubl quoted array..I am getting the output as comma separated values whic is incorrec result..the result should looks like "if the values have sequenced numbers then it should '-' with separator otherwise it has to display the number".
Try this: (Note: I dont have 11G R2, so I used wm_concat to test it, which is not supported, but if you have 11G R2, then use LISTAGG)
WITH TABIBITOSAN
AS (SELECT
ACCOUNT_REF,
"number",
TO_NUMBER ( "number" )
- ROW_NUMBER ( ) OVER (ORDER BY TO_NUMBER ( "number" ))
AS GRP
FROM
TBL),
DATASET1
AS (SELECT
ACCOUNT_REF,
CASE
WHEN MIN ( "number" ) = MAX ( "number" )
THEN
MIN ( "number" )
ELSE
MIN ( "number" )
|| '-'
|| MAX ( "number" )
END
AS RANGES
FROM
TABIBITOSAN
GROUP BY
ACCOUNT_REF,
GRP
ORDER BY
ACCOUNT_REF,
MIN ( "number" ))
SELECT
ACCOUNT_REF,
WM_CONCAT ( RANGES ) AS NEW_RANGES
FROM
DATASET1
GROUP BY
ACCOUNT_REF;
Results:
101 002-004,006-008,011
Using LISTAGG
SELECT
ACCOUNT_REF,
LISTAGG ( RANGES,
',' )
WITHIN GROUP (ORDER BY RANGES)
AS NEW_RANGES
FROM
DATASET1
WHERE
ACCOUNT_REF = ACC.ACCOUNT_REF
GROUP BY
ACCOUNT_REF