EXISTS inside CASE inside AGGREGATE FUNCTION - sql

I'm trying to do something similar to this:
SELECT SUM(
CASE WHEN (<some_condition>
AND EXISTS(SELECT 1
FROM <tableA> as tA
WHERE tA.Id = tB.tAId and
<some_other_condition>
)
) THEN 1 ELSE 0 END
) as <column_name>
FROM <tableB> as tB
I need to avoid the use of joins to achieve what I need, because I don't want to count/sum duplicates returned by the results I get through join clauses in this case. When I try to execute my query, I get the following error message:
"cannot perform an aggregate function on an expression containing an aggregate or a subquery"
I don't know what I am doing wrong.
Any help appreciated.
EDIT: I added a possible condition on the WHERE statement only to show I need to relate the columns of the outside table (tableB) with the inside table (tableA)
Here's an example of tableA and tableB and what happens when I JOIN both:
tableA: tableB: tableA JOIN tableB
Id Id tAId Time Id(A) Id(B) taId Time
1 1 1 5 1 1 1 5
2 2 2 6 2 2 2 6
3 3 2 10 2 3 2 10
4 4 3 11 3 4 3 11
5 5 4 14 4 5 4 14
6 4 13 4 6 4 13
7 4 12 4 7 4 12
8 5 10 5 8 5 10
So, when I try to count/sum some property (i.e. tableA.Id) from tableA using the results of the JOIN operation as input, I end up counting duplicates. Here's why I can't use JOIN unless I can select DISTINCT elements. I liked the answer that shows this so I'm trying to implement that with no errors.
(Note: I really appreciate all the other answers since they show possible solutions that can fit my situation)

SELECT SUM(
CASE WHEN (<some_condition>
AND tA.Id IS NOT NULL
) THEN 1 ELSE 0 END
) as <column_name>
FROM <tableB> as tB
JOIN (SELECT DISTINCT ta.Id
FROM <tableA>) as tA ON tA.Id = tB.Id AND <some_other_condition>

Since, as the message says, you cannot use a subquery in an aggregate function (SUM()), consider using a join similar to the following:
SELECT SUM(
CASE WHEN (<some_condition>
AND tA.<matching_column> IS NOT NULL
) THEN 1 ELSE 0 END
) as <column_name>
FROM <tableB> tB
LEFT JOIN <tableA> tA
ON tA.Id = tB.Id
WHERE tA.<some_other_condition>

Just a simple answer from what you provided initiall:
SELECT SUM(<column_name>) AS SumColumn
FROM (
SELECT CASE WHEN <some_condition> AND EXISTS(SELECT 1 FROM <tableA> WHERE <some_other_condition>) THEN 1 ELSE 0 END as <column_name>
FROM <tableB>
)AS SUB

Your query is an aggregation query, so the intent appears to be returning one row. So, just move all the conditions to the WHERE clause. This is my best guess as to your intent:
SELECT COUNT(*) as <column_name>
FROM <tableB> tB
WHERE (<some_condition> AND
EXISTS (SELECT 1
FROM <tableA> as tA
WHERE tA.Id = tB.Id AND <some_other_condition>
);

Related

Find rows that contains same value on different columns

The table to find which rows contains same value on two different columns for 2 rows. Here is a small sample rows among 2k+ rows.
id left right
1 3 4
2 4 1
3 1 9
4 2 6
5 2 5
6 9 8
7 0 7
In the above case, I need to get row 1,2,3,6 as it contains 4 on two rows of two different columns i.e (id=1&2),1 on two rows of two different columns(id=1&3) and 9 on two rows of two different columns(id=3&6)
My thoughts:
I did thought many things for example cross join on left and right column, group by and count etc.
with Final as (With OuterTable as (WITH Alias AS (SELECT id as left_id , left FROM Test)
SELECT DISTINCT id, left_id FROM Alias
INNER JOIN Test ON Alias.left = Test.right)
SELECT id from OuterTable
UNION ALL
SELECT left_id from OuterTable)
SELECT DISTINCT * from Final;
It's messy, but it works.
You can do it with EXISTS:
SELECT t1.*
FROM tablename t1
WHERE EXISTS (
SELECT 1 FROM tablename t2
WHERE t1.id <> t2.id AND (t2.left = t1.right OR t1.left = t2.right)
)
See the demo.
Results:
id
left
right
1
3
4
2
4
1
3
1
9
6
9
8

How to use a new column as a flag to show Null row?

I have table A:
id
1
2
3
4
5
and table B:
id
2
3
4
I left join A and B:
id id
1 NULL
2 2
3 3
4 4
5 NULL
And how can I get a new column like this:
id id flag
1 NULL 0
2 2 1
3 3 1
4 4 1
5 NULL 0
Generally speaking, I want all rows in A but not in B to be flaged as 0 and want all rows in both tables to be flaged as 1. How can I achieve that? Better not use CTE.
This is just a CASE expression:
CASE WHEN B.id IS NULL THEN 0 ELSE 1 END AS flag
Alternatively, you could use an IIF (which is shorthand CASE expression):
IIF(b.id IS NULL, 0,1)
I would recommend using exists:
select a.*,
(case when exists (select 1 from b where b.id = a.id
then 1 else 0
end) as flag
from a;
The purpose of using exists instead of left join is that you are guaranteed to not get duplicate rows -- even if ids are duplicated in b. That is a nice guarantee.
From a performance perspective, the two should be similar, but it is possible that the case is an iota faster.

SQL: How to count instances of Column A in Column B

I have a table with 2 columns A, and B that represent a connection graph between the two.
A B
1 3
2 5
4 2
3 5
2 3
I need to find how many instances of column A occur in column B (including 0)
So for the example above I would need the result set
A OccurencesInB
1 0
2 1
3 2
4 0
The best I have so far is
SELECT B, COUNT(*) AS TABLE_COUNT
FROM TABLE
GROUP BY B
ORDER BY TABLE_COUNT DESC
This does not find the instances of A that do not occur in B, which is crucial.
Any assistance will be greatly appreciated!
Use a correlated sub-query:
SELECT A,
TABLE_COUNT = (SELECT COUNT(*)
FROM TableName t2
WHERE t2.B = t1.A)
FROM TableName t1
GROUP BY A
ORDER BY TABLE_COUNT DESC, A
Result:
A TABLE_COUNT
3 2
2 1
1 0
4 0

To find total number of rows

I have a table like this
Table1
=======
A B
8 5
2 9
null 4
2 5
How to find total number of rows from table1,note if any column value is null in a row then that row should be considered as 2 rows?
I have tried with count(*)*2 and nvl function it doesn't work
Try this
SELECT SUM(CASE WHEN A IS NULL OR B IS NULL THEN 2 ELSE 1 END) AS CountVal
FROM TABLE1
Fiddle Demo
O/P:
COUNTVAL
--------
5
COUNT() is rowbased.. you can tweak it using SUM() instead..
select sum(NVL2(a,NVL2(b,1,2),2)) FROM TABLE1
CASE as suggested by #Vignesh is the simplest and more readable !!
COUNT() can also done like this.. But NOT a optimal solution at all!
SELECT COUNT(1) FROM
(
SELECT NVL(a,NVL(b,1)) FROM TABLEA
UNION ALL
SELECT NVL(a,NVL(b,1)) FROM TABLEA
WHERE A OR B iS NULL
)

Copying existing rows in a table

How do I go about doing the following?
I am using the following query to get a specific users tab ids:
select id
from intranet.dbo.tabs
where cms_initials = #user
order by id asc
which might return the following ids
4
5
6
7
I now want to insert the rows from the following query:
select tabs_id, widgets_id, sort_column, sort_row
from intranet.dbo.columns c
inner join intranet.dbo.tabs t on c.tabs_id = t.id
where t.is_default = 1
But use the ids from the first query to replace the tab ids
so if the second query originally returns tabs_id's as
0
0
0
0
1
1
1
2
2
2
3
3
I should end up with
0
0
0
0
1
1
1
2
2
2
3
3
4
4
4
4
5
5
5
6
6
6
7
7
Is this possible with sql server 2005 without using stored procedures?
So far I have
insert into intranet.dbo.columns ( tabs_id, widgets_id, sort_column, sort_row )
select tabs_id, widgets_id, sort_column, sort_row
from intranet.dbo.columns c
inner join intranet.dbo.tabs t on c.tabs_id = t.id
where t.is_default = 1
But this just copies everything as is, I need to do that, but replace the ids in the copied rows.
This solution uses common table expressions and ranking functions. A and B are your original queries ranked by tab order. A and B are then joined by tab ranking and inserted.
USE intranet
;WITH A AS
(
SELECT ROW_NUMBER() OVER (ORDER BY id) AS tab_ranking
, id
FROM dbo.tabs
WHERE cms_initials = #user
),
B AS
(
SELECT DENSE_RANK() OVER (ORDER BY tabs_id) AS tab_sequence
, tabs_id, widgets_id, sort_column, sort_row
FROM dbo.columns
WHERE tabs_id IN (SELECT t.id FROM dbo.tabs t WHERE t.is_default = 1)
)
INSERT INTO dbo.columns (tabs_id, widgets_id, sort_column, sort_row)
SELECT a.id, b.widgets_id, b.sort_column, b.sort_row
FROM A
INNER JOIN B ON B.tab_ranking = A.tab_ranking