oracle counting null value, which is more appropriate? - sql

Oracle Counting Null, both of them are returning same result, but which is more recommended? or is there better way?
COUNT(DECODE(RESP_CD, NULL, 'X'))
vs
NVL(SUM(DECODE(RESP_CD, NULL, 1, 0)), 0)

SELECT SUM(n_count), SUM(x_count)
FROM
(SELECT CASE WHEN resp_cd IS NULL THEN 1 END n_count,
CASE WHEN resp_cd = 'x' THEN 1 END x_count
FROM your_table
);

Related

sql SERVER - distinct selection based on priority columns

hello I would like to find a solution to solve my problem in a single request if possible.
For the moment I take all the records then I go through the lines one by one to eliminate what I don't want.
I have 2 tables : first table with links
the second with the prefered label for the url
the second table must be consulted keeping only the row with maximum priority
priority rules are
the current user then
the user group and finally
everyone.
if the hidden column is true, exclude any reference to the url
here is the expected result.
Unfortunately, I don't see any other solution than to multiply the conditions on several selects and unions.
if you have a idea to solve my problem, thank you in advance for your help
It appears as though you can rely on pref_id for the preference ordering, correct? If so, you could try:
SELECT *
FROM table2
INNER JOIN table1 ON table2.url_id = table1.url_id
QUALIFY ROW_NUMBER() OVER (
PARTITION BY table1.url
ORDER BY pref_id ASC
) = 1
This will partition by the url and then provide only the one with lowest pref_id.
I didn't test this SQL as I wasn't sure which RDBMS you're running on, but I used Rasgo to translate the SQL.
maybe of interest in this tricky query:
select so.*, table1.url from
(select distinct t.url_id,
(select pref_id from table2 s where s.url_id = t.url_id order by "user" is null, "group" is null limit 1) pref_id
from table2 t
where not exists(select 1 from table2 s where s.hide and s.url_id = t.url_id)
) ids
join table2 so on so.pref_id = ids.pref_id
join table1 ON table1.url_id = ids.url_id
order by so.url_id;
here is my solution but i think there is better to do.
in the condition's select, I built a column which gives a level note according to the priorities
DECLARE #CUR_USER VARCHAR(10) = 'ROBERT'
DECLARE #CUR_GROUP VARCHAR(10) = 'DEV'
DECLARE #TABLE1 TABLE (
URL_ID INT
,URLNAME VARCHAR(100)
);
DECLARE #TABLE2 TABLE (
PREF_ID INT
,URL_ID INT
,FAVORITE_LABEL VARCHAR(100)
,USER_GROUP VARCHAR(10)
,USER_CODE VARCHAR(10)
,HIDE_URL DECIMAL(1, 0) DEFAULT 0
);
INSERT INTO #TABLE1
VALUES
(1, 'https://stackoverflow.com/')
,(2, 'https://www.microsoft.com/')
,(3, 'https://www.apple.com/')
,(4, 'https://www.wikipedia.org/')
;
INSERT INTO #TABLE2
VALUES
(1000, 1, 'find everything', NULL, 'ROBERT', 0)
,(1001, 1, 'a question ? find the answer here', 'DEV', NULL, 0)
,(1002, 1, 'StackOverFlow', NULL, NULL, 0)
,(1003, 2, 'Windows', 'DEV', NULL, 0)
,(1004, 2, 'Microsoft', NULL, NULL, 0)
,(1005, 3, 'Apple', NULL, NULL, 0)
,(1006, 4, 'Free encyclopedia', NULL, 'ROBERT', 1)
,(1007, 4, 'Wikipedia', NULL, NULL, 0)
,(1008, 1, 'StackOverFlow FOR MAT', 'MAT', NULL, 0)
,(1009, 2, 'Microsoft FOR MAT', 'MAT', NULL, 0)
,(1010, 3, 'Apple', 'MAT', NULL, 1)
,(1011, 4, 'Wikipedia FOR MAT', 'MAT', NULL, 0)
,(1012, 1, 'StackOverFlow', NULL, 'JEAN', 1)
,(1013, 2, 'Microsoft ', NULL, 'JEAN', 0)
,(1014, 3, 'Apple', NULL, 'JEAN', 0)
,(1015, 4, 'great encyclopedia', NULL, 'JEAN', 0)
;
SELECT t2.* ,t1.URLName
FROM #TABLE1 t1
INNER JOIN #TABLE2 t2 ON t1.URL_ID = t2.URL_ID
WHERE EXISTS (
SELECT 1
FROM (
SELECT TOP (1) test.PREF_ID
,CASE
-- if I do not comment this case: jean from the MAT group will not see apple
-- WHEN Hide_Url = 1
-- THEN 3
WHEN USER_code IS NOT NULL
THEN 2
WHEN USER_GROUP IS NOT NULL
THEN 1
ELSE 0
END AS ROW_LEVEL
FROM #TABLE2 test
WHERE (
(
test.USER_GROUP IS NULL
AND test.user_group IS NULL
AND test.USER_code IS NULL
)
OR (test.USER_GROUP = #CUR_GROUP)
OR (test.USER_code = #CUR_USER)
)
AND t2.URL_ID = test.URL_ID
ORDER BY ROW_LEVEL DESC
) test
WHERE test.PREF_ID = t2.PREF_ID
AND Hide_Url = 0
)
Simply use an ORDER BY clause that puts the preferred row first. You can use this in the window function ROW_NUMBER and work with this or use a lateral top(1) join with CROSS APPLY.
select *
from urls
cross apply
(
select top(1) *
from labels
where labels.url_id = urls.url_id
where [Group] is not null or [user] is not null or hide is not null
order by
case when [Group] is null then 2 else 1 end,
case when [user] is null then 2 else 1 end,
case when hide is null then 2 else 1 end
) top_labels
order by urls.url_id;

Subquery failure: This type of correlated subquery pattern is not supported due to internal error;

I'm trying to run the following query on Redshift but I keep getting this error:
Amazon Invalid operation: This type of correlated subquery pattern is not supported due to internal error;
The query is as follow:
INSERT INTO table_tmp (
-- straightforward table's schema, fields, datatypes
-- the problem is with the SELECT statement that follows
)
SELECT
CHARGEPOINT.Id,
'ABC',
CHARGEPOINT.CurrentStatus,
NULL,
NULL,
CHARGEPOINT.ChargingStationOwner,
CHARGEPOINT.CPOName,
CASE
WHEN CHARGEPOINT.CurrentStatus = 'Offline' OR CHARGEPOINT.CurrentStatus = 'Unavailable' OR CHARGEPOINT.CurrentStatus = NULL THEN 'Red'
WHEN CHARGEPOINT.CurrentStatus = 'Pending' OR CHARGEPOINT.CurrentStatus = 'Unmanaged' THEN 'Amber'
WHEN CHARGEPOINT.CurrentStatus = 'Online' OR CHARGEPOINT.CurrentStatus = 'Available' THEN 'Green'
END,
NULL,
CAST (CHARGEPOINT.InstallationDate AS timestamp),
NULL,
CHARGEPOINT.FirmwareVersion,
NULL,
CHARGEPOINT.ManufacturerName,
NULL,
CASE
WHEN CHARGEPOINT.CurrentStatus = 'Online' OR CHARGEPOINT.CurrentStatus = 'Available' THEN TRUE
ELSE FALSE
END,
NULL,
(SELECT count(CHARGEPOINT_GROUP.Id) FROM x.chargepoint CHARGEPOINT_GROUP GROUP BY CHARGEPOINT.Id),
CHARGEPOINT.IP,
NULL,
CAST (CHARGEPOINT.CreatedAt AS timestamp),
CASE
WHEN CHARGEPOINT.CurrentStatus = 'Offline' OR CHARGEPOINT.CurrentStatus = 'Unavailable' OR CHARGEPOINT.CurrentStatus = NULL THEN TRUE
ELSE FALSE
END,
NULL,
NULL,
CHARGEPOINT.Label,
NULL,
CHARGEPOINT.SAPID,
CHARGEPOINT.Serialnumber,
NULL,
NULL,
NULL,
CHARGEPOINT.IMSI,
CHARGEPOINT.ICCID,
CAST (CHARGEPOINT.LastHeartBeat AS timestamp),
'UPDATE',
current_timestamp,
'ABC',
'',
'',
current_timestamp,
current_timestamp,
TRUE
FROM x.chargept CHARGEPOINT;
After some research on previous similar questions, I'm now suspecting that this subquery is the cause of this issue :
(SELECT count(CHARGEPOINT_GROUP.Id) FROM x.chargepoint CHARGEPOINT_GROUP GROUP BY CHARGEPOINT.Id)
So I've tried using a WITH statement and do this instead:
(WITH cte1 AS (
(SELECT count(CHARGEPOINT_GROUP.Id)
FROM ext_transform_latest_htb.chargepoint CHARGEPOINT_GROUP
GROUP BY CHARGEPOINT.Id))
SELECT * from cte1),
But I got the same error as above.
Any guidance is much appreciated!
yes it is the problem , because its returning more than 1 row. so how about if you change the subquery to this and give it an alias:
...
, (
SELECT
count(CHARGEPOINT_GROUP.Id)
FROM
x.chargepoint CHARGEPOINT_GROUP
WHERE CHARGEPOINT.Id = CHARGEPOINT_GROUP.chargeptid -- < or whatever FK is
) as ChargePointCount
, ...

Convert multiple bit columns into one field

I have the following table that contains multiple bit columns, and what I'm wanting to do is convert it for display purposes with a hard-coded label based on the bit column values. This is shown in the highlighted yellow column name expectedresult.
Test Data
CREATE TABLE [dbo].[testbit](
[StaffId] [int] NULL,
[type1] [bit] NOT NULL,
[type2] [bit] NULL,
[type3] [bit] NULL,
[type4] [bit] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[testbit] ([StaffId], [type1], [type2], [type3], [type4]) VALUES (1, 1, 0, 0, 1)
GO
INSERT [dbo].[testbit] ([StaffId], [type1], [type2], [type3], [type4]) VALUES (2, 0, 1, 0, 0)
GO
INSERT [dbo].[testbit] ([StaffId], [type1], [type2], [type3], [type4]) VALUES (3, 1, 1, 1, 1)
GO
Use a CASE WHEN <expr> or CASE <expr> WHEN <value> expression, then concatenate in an outer-query with CONCAT_WS.
A CASE expression evaluates to NULL when an ELSE case is omitted, so these expressions are equivalent:
CASE x WHEN 1 THEN 'foo' END
CASE x WHEN 1 THEN 'foo' ELSE NULL END
CASE WHEN x = 1 THEN 'foo' END
CASE WHEN x = 1 THEN 'foo' ELSE NULL END
The CONCAT_WS function is variadic and concatenates non-NULL strings with the separator string (the first argument).
CONCAT_WS( ', ', 'a', 'b', NULL, NULL, 'c' ) == 'a, b, c'
Here's how I'd do it:
SELECT
StaffId,
CONCAT_WS( ', ', Type1Text, Type2Text, Type3Text, Type4Text ) AS TypeNames
FROM
(
SELECT
StaffId,
CASE type1 WHEN 1 THEN 'Type1Name' END AS Type1Text,
CASE type2 WHEN 1 THEN 'Type2Name' END AS Type2Text,
CASE type2 WHEN 1 THEN 'Type3Name' END AS Type3Text,
CASE type3 WHEN 1 THEN 'Type4Name' END AS Type4Text
FROM
...
) AS iq
The outer query can be elided if you don't mind making it slightly harder to read:
SELECT
StaffId,
CONCAT_WS( ', ',
CASE type1 WHEN 1 THEN 'Type1Name' END AS Type1Text,
CASE type2 WHEN 1 THEN 'Type2Name' END AS Type2Text,
CASE type2 WHEN 1 THEN 'Type3Name' END AS Type3Text,
CASE type3 WHEN 1 THEN 'Type4Name' END AS Type4Text
) AS TypeNames
FROM
...

How to count null columns in a row?

How to count all the columns in a table that have a null value?
The table having a large number of columns and the method should iterate over the columns in a dynamic manner.
In any given row (selected by an identifier), count the null cells.
Select count(number of null value cells) where id=1 from table
e.g:
I have a table consisting of 200 columns I want to know how many null
cells does the row with id=1 have
Basically, you need to check every column in a selected row if it's null or not.
Since you have 200+ columns in your table, manual approach seems tedious, so you can automate it a little bit and construct the query dynamically by querying user_tab_columns:
-- set up
create table t1(
rid number primary key,
c1 varchar2(17),
c2 date,
c3 timestamp,
c4 number
);
insert into t1
valueS(1, 'string', null, systimestamp, null);
commit ;
-- going to use DECODE function - doesnt require type consistency.
select 'decode('||column_name||', null, 1, 0)+' as res
from user_tab_columns
where table_name = 'T1'
Result:
RES
------------------------------
decode(RID, null, 1, 0)+
decode(C1, null, 1, 0)+
decode(C2, null, 1, 0)+
decode(C3, null, 1, 0)+
decode(C4, null, 1, 0)+
And final query:
select decode(C4, null, 1, 0)+
decode(C3, null, 1, 0)+
decode(C2, null, 1, 0)+
decode(C1, null, 1, 0)+
decode(RID, null, 1, 0) as num_of_nulls
from t1
where rid = 1
Result:
NUM_OF_NULLS
--------------
2
Try this. You can pass any ID and get the number of columns with NULL value.
CREATE TABLE TEST (ID NUMBER, B VARCHAR2(20), C NUMBER, D VARCHAR2(200));
INSERT INTO TEST VALUES (1,NULL,NULL,'XX');
SELECT COUNT(NULL_COLS)
FROM (
SELECT
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('SELECT CASE WHEN '||COLUMN_NAME||' IS NULL THEN 0 ELSE NULL END COL_VAL FROM '||TABLE_NAME||' WHERE ID=&VALUE')),'/ROWSET/ROW/COL_VAL')) NULL_COLS
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME='TEST');

How can get null column after UNPIVOT?

I have got the following query:
WITH data AS(
SELECT * FROM partstat WHERE id=4
)
SELECT id, AVG(Value) AS Average
FROM (
SELECT id,
AVG(column_1) as column_1,
AVG(column_2) as column_2,
AVG(column_3) as column_3
FROM data
GROUP BY id
) as pvt
UNPIVOT (Value FOR V IN (column_1,column_2,column_3)) AS u
GROUP BY id
if column_1,column_2 and column_3 (or one of this columns) have values then i get result as the following:
id, Average
4, 5.12631578947368
if column_1,column_2 and column_3 have NULL values then the query does not return any rows as the following:
id, Average
my question is how can i get as the following result if columns contents NULL values?
id, Average
4, NULL
Have you tried using COALESCE or ISNULL?
e.g.
ISNULL(AVG(column_1), 0) as column_1,
This does mean that you will get 0 as the result instead of 'NULL' though - do you need null when they are all NULL?
Edit:
Also, is there any need for an unpivot? Since you are specifying all 3 columns, why not just do:
SELECT BankID, (column_1 + column_2 + column_3) / 3 FROM partstat
WHERE bankid = 4
This gives you the same results but with the NULL
Of course this is assuming you have 1 row per bankid
Edit:
UNPIVOT isn't supposed to be used like this as far as I can see - I'd unpivot first then try the AVG... let me have a go...
Edit:
Ah I take that back, it is just a problem with NULLs - other posts suggest ISNULL or COALESCE to eliminate the nulls, you could use a placeholder value like -1 which could work e.g.
SELECT bankid, AVG(CASE WHEN value = -1 THEN NULL ELSE value END) AS Average
FROM (
SELECT bankid,
isnull(AVG(column_1), -1) as column_1 ,
AVG(Column_2) as column_2 ,
Avg(column_3) as column_3
FROM data
group by bankid
) as pvt
UNPIVOT (Value FOR o in (column_1, column_2, column_3)) as u
GROUP BY bankid
You need to ensure this will work though as if you have a value in column2/3 then column_1 will no longer = -1. It might be worth doing a case to see if they are all NULL in which case replacing the 1st null with -1
Here is an example without UNPIVOT:
DECLARE #partstat TABLE (id INT, column_1 DECIMAL(18, 2), column_2 DECIMAL(18, 2), column_3 DECIMAL(18, 2))
INSERT #partstat VALUES
(5, 12.3, 1, 2)
,(5, 2, 5, 5)
,(5, 2, 2, 2)
,(4, 2, 2, 2)
,(4, 4, 4, 4)
,(4, 21, NULL, NULL)
,(6, 1, NULL, NULL)
,(6, 1, NULL, NULL)
,(7, NULL, NULL, NULL)
,(7, NULL, NULL, NULL)
,(7, NULL, NULL, NULL)
,(7, NULL, NULL, NULL)
,(7, NULL, NULL, NULL)
;WITH data AS(
SELECT * FROM #partstat
)
SELECT
pvt.id,
(ISNULL(pvt.column_1, 0) + ISNULL(pvt.column_2, 0) + ISNULL(pvt.column_3, 0))/
NULLIF(
CASE WHEN pvt.column_1 IS NULL THEN 0 ELSE 1 END +
CASE WHEN pvt.column_2 IS NULL THEN 0 ELSE 1 END +
CASE WHEN pvt.column_3 IS NULL THEN 0 ELSE 1 END
, 0)
AS Average
FROM (
SELECT id,
AVG(column_1) as column_1,
AVG(column_2) as column_2,
AVG(column_3) as column_3
FROM data
GROUP BY id
) as pvt