Is this query possible in sqlite? - sql

I have a table like this:
ID A B C
0 x x
1 x
2 x x
3 x
I would like to obtain this with a query (SQLite syntax if possible)
A 3
B 2
C 1
I've been using CASE WHEN clause, like this
WITH solutions AS (SELECT
CASE
WHEN `B` = 'x' THEN 'A'
WHEN `B` = 'x' THEN 'B'
WHEN `C` = 'x' THEN 'C'
END AS 'Solution'
FROM use_cases)
SELECT solution, COUNT(*) AS 'Count'
FROM solutions
GROUP BY Solution ORDER BY COUNT(*) DESC
But it won't work in case a row present more than one column with 'x', as the WHEN evaluation will stop at the first case is found.

Use union all:
select col, count(*)
from (select 'A' as col from t where A = 'x' union all
select 'B' from t where B = 'x' union all
select 'C' from t where C = 'x'
) x
group by col
order by count(*) desc;

You need UNION ALL:
select 'A' col1, count(A = 'x') col2 from tablename
union all
select 'B', count(B = 'x') from tablename
union all
select 'C', count(C = 'x') from tablename
See the demo.
Or with a CTE to avoid multiple scans of the table:
with cte as (
select count(A = 'x') cola, count(B = 'x') colb, count(C = 'x') colc
from tablename
)
select 'A' col1, cola col2 from cte
union all
select 'B', colb from cte
union all
select 'C', colc from cte
See the demo.
Results:
| col1 | col2 |
| ---- | ---- |
| A | 3 |
| B | 2 |
| C | 1 |

If you specifically wanted to check for the X then you could take advantage of the fact that (at least in SQLite) TRUE is equivalent to 1 and FALSE to 0 so something simple like the following would work:
SELECT 'A', sum(A = 'x') from MyTable
UNION ALL
SELECT 'B', sum(B = 'x') from MyTable
UNION ALL
SELECT 'C', sum(C = 'x') from MyTable;
Another simple solution is to rely on the fact that count() only counts non-null values. So if the columns contain NULL in the places where it doesn't contain an X, then this simple SQL will work in SQLite:
SELECT 'A', count(A) from MyTable
UNION ALL
SELECT 'B', count(B) from MyTable
UNION ALL
SELECT 'C', count(C) from MyTable;
A slight variant of the above ... If the columns that don't contain X have something in them other than NULL … then just convert that to null. For example if the columns that don't contain an X happen to contain a space then the following would work:
SELECT 'A', count(nullif (A, ' ')) from MyTable
UNION ALL
SELECT 'B', count(nullif (B, ' ')) from MyTable
UNION ALL
SELECT 'C', count(nullif (C, ' ')) from MyTable;
Note: My proposed approaches will likely require multiple scans of the table but with proper indexing such a penalty could be mitigated. It is possible that CTE-based solutions may be more efficient but you would have to test both approaches to be sure. CTE-based solutions may involve the creation of temp tables (or temp result sets) from potentially multiple subqueries. All of which can make CTEs less efficient in certain circumstances … but YMMV.

Try this
Select val, cnt from ( SELECT 'A' as val,A,count(*) as cnt FROM TABLE
GROUP BY A
UNION
SELECT 'B' as val,B,count(*) as cnt FROM TABLE
Group by B
UNION
SELECT 'C' as val,C,count(*) as cnt FROM TABLE
Group by C))
Check below output
or if you want to exclude empty space count then
SELECT VAL,CNT FROM(SELECT 'A' AS VAL,A as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY A
UNION
SELECT 'B' AS VAL,B as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY B
UNION
SELECT 'C' AS VAL,C as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY C
) where col !=' ';

Related

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

Selecting distinct values within a a group

I want to select distinct values of one variable within a group defined by another variable. What is the easiest way?
My first thought was to combine group by and distinct but it does not work. I tried something like:
select distinct col2, col1 from myTable
group by col1
I have looked at this one here but can't seem to solve my problem
Using DISTINCT along with GROUP BY in SQL Server_
Table example
If your requirement is to pick distinct combinations if col1 and COL2 then no need to group by just use
SELECT DISTINCT COL1, COL2 FROM TABLE1;
But if you want to group by then automatically one record per group is displayed by then you have to use aggregate function of one of the columns i.e.
SELECT COL1, COUNT(COL2)
FROM TABLE1 GROUP BY COL1;
no need group by just use distinct
select distinct col2, col1 from myTable
create table t as
with inputs(val, id) as
(
select 'A', 1 from dual union all
select 'A', 1 from dual union all
select 'A', 2 from dual union all
select 'B', 1 from dual union all
select 'B', 2 from dual union all
select 'C', 3 from dual
)
select * from inputs;
The above creates your table and the below is the solution (12c and later):
select * from t
match_recognize
(
partition by val
order by id
all rows per match
pattern ( a {- b* -} )
define b as val = a.val and id = a.id
);
Output:
Regards,
Ranagal

Determine SQL From clause at runtime?

I have a table1 which contains a column where it stores other table's name.
Based on the value in table1, the query should pull out data corresponding to the table name given in it.
For example , let the table which stores tablename be tablelist(tablename,tableid)
Let the other tables whose names stored in tablelist.tablename be A, B , C
Based on a given input parameter tableid,
If the value stored in tablename is 'A' the query should pull out results equivalent to :
Select A.name from A;
If its 'B', the query should be :
Select B.type from B;
If its 'C' , the query should be :
Select C.msg from C;
How will I make it into a single query which accepts the table id as input ?
Please advice
You could try case when construction:
select case tableid
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end
from tbl
Example with some data:
with
tablelist(tablename, tableid) as (
select 'A', 1 from dual union all
select 'B', 2 from dual union all
select 'B', 7 from dual union all
select 'C', 3 from dual ),
a(name) as (select 'Chris' from dual),
b(type) as (select 'T800' from dual),
c(msg) as (select 'Hello' from dual)
select case tablename
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end as value
from tablelist
where tableid = 7
Result T800.

How to select the records that are sharing the same Id but with different Name?

I have a table like below:
Col_Name Col_Id Col_Flag
A 1 1
A 1 2
B 1 1
C 2 1
C 2 2
For those records, I'd like to get the result for which Col_Id are sharing by differnet Col_Name, for the example above, the desired result is to return Col_Id as 1, because it is sharing by A and B, and I do not want Col_Id = 2 get returned because only C is using this value.
And, the total number of the same Col_Name that are using the same Col_Id is not limited to 2, meaning the Col_Flag could be larger than 2.
Any suggestions will be much appreciated. Thanks.
SELECT COL_ID,count(distinct COL_NAME) FROM TABLE
GROUP BY COL_ID
having count(distinct COL_NAME)>1
Use a self-join on col_id and a WHERE clause to limit the output to those cases with a different col_name:
select distinct
a.col_id
from
some_table as a
inner join some_table as b on a.col_id=b.col_id
where
a.col_name <> b.col_name;
Try this
SELECT COL_ID FROM test9
GROUP BY COL_ID
having COUNT(DISTINCT COL_Name)>1
;WITH CTE(COL_NAME,COL_ID,COL_FLAG)
AS
(
SELECT 'A', 1,1 UNION ALL
SELECT 'A', 1,2 UNION ALL
SELECT 'B', 1,1 UNION ALL
SELECT 'C', 2,1 UNION ALL
SELECT 'C', 2,2
)
SELECT COL_ID,
COL_NAME FROM
(
SELECT COL_ID,COL_NAME,
COUNT(COL_NAME)OVER(PARTITION BY COL_ID,COL_NAME ORDER BY COL_NAME) AS UNIQENAME
FROM CTE
)DT
WHERE DT.UNIQENAME=1

Select query select based on a priority

Someone please change my title to better reflect what I am trying to ask.
I have a table like
Table (id, value, value_type, data)
ID is NOT unique. There is no unique key.
value_type has two possible values, let's say A and B.
Type B is better than A, but often not available.
For each id if any records with value_type B exists, I want all the records with that id and value_type B.
If no record for that id with value_Type B exists I want all records with that id and value_type A.
Notice that if B exists for that id I don't want records with type A.
I currently do this with a series of temp tables. Is there a single select statement (sub queries OK) that can do the job?
Thanks so much!
Additional details:
SQL Server 2005
RANK, rather than ROW_NUMBER, because you want ties (those with the same B value) to have the same rank value:
WITH summary AS (
SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM TABLE t
WHERE t.value_type IN ('A', 'B'))
SELECT s.id,
s.value,
s.value_type,
s.data
FROM summary s
WHERE s.rank = 1
Non CTE version:
SELECT s.id,
s.value,
s.value_type,
s.data
FROM (SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM TABLE t
WHERE t.value_type IN ('A', 'B')) s
WHERE s.rank = 1
WITH test AS (
SELECT 1 AS id, 'B' AS value_type
UNION ALL
SELECT 1, 'B'
UNION ALL
SELECT 1, 'A'
UNION ALL
SELECT 2, 'A'
UNION ALL
SELECT 2, 'A'),
summary AS (
SELECT t.*,
RANK() OVER (PARTITION BY t.id
ORDER BY t.value_type DESC) AS rank
FROM test t)
SELECT *
FROM summary
WHERE rank = 1
I get:
id value_type rank
----------------------
1 B 1
1 B 1
2 A 1
2 A 1
SELECT *
FROM table
WHERE value_type = B
UNION ALL
SELECT *
FROM table
WHERE ID not in (SELECT distinct id
FROM table
WHERE value_type = B)
The shortest query to do the job I can think of:
SELECT TOP 1 WITH TIES *
FROM #test
ORDER BY Rank() OVER (PARTITION BY id ORDER BY value_type DESC)
This is about 50% worse on CPU as OMG Ponies' and Christoperous 5000's solutions, but the same number of reads. It's the extra sort that is making it take more CPU.
The best-performing original query I've come up with so far is:
SELECT *
FROM #test
WHERE value_type = 'B'
UNION ALL
SELECT *
FROM #test T1
WHERE NOT EXISTS (
SELECT *
FROM #test T2
WHERE
T1.id = T2.id
AND T2.value_type = 'B'
)
This consistently beats all the others presented on CPU by about 1/3rd (the others are about 50% more) but has 3x the number of reads. The duration on this query is often 2/3rds the time of all the others. I consider it a good contender.
Indexes and data types could change everything.
declare #test as table(
id int , value [nvarchar](255),value_type [nvarchar](255),data int)
INSERT INTO #test
SELECT 1, 'X', 'A',1 UNION
SELECT 1, 'X', 'A',2 UNION
SELECT 1, 'X', 'A',3 UNION
SELECT 1, 'X', 'A',4 UNION
SELECT 2, 'X', 'A',5 UNION
SELECT 2, 'X', 'B',6 UNION
SELECT 2, 'X', 'B',7 UNION
SELECT 2, 'X', 'A',8 UNION
SELECT 2, 'X', 'A',9
SELECT * FROM #test x
INNER JOIN
(SELECT id, MAX(value_type) as value_type FROM
#test GROUP BY id) as y
ON x.id = y.id AND x.value_type = y.value_type
Try this (MSSQL).
Select id, value_typeB, null
from myTable
where value_typeB is not null
Union All
Select id, null, value_typeA
from myTable
where value_typeB is null and value_typeA is not null
Perhaps something like this:
select * from mytable
where id in (select distinct id where value_type = "B")
union
select * from mytable
where id in (select distinct id where value_type = "A"
and id not in (select distinct id where value_type = "B"))
This uses a union, combining all records of value B with all records that have only A values:
SELECT *
FROM mainTable
WHERE value_type = B
GROUP BY value_type UNION SELECT *
FROM mainTable
WHERE value_type = A
AND id NOT IN(SELECT *
FROM mainTable
WHERE value_type = B);