SQL Server: how to include count in the select query with where clause? - sql

Let's say I have this table:
id out flag
--- --- ---
1 0 1
1 1 0
1 4 0
2 0 1
2 2 0
2 2 0
3 0 1
3 2 0
3 1 0
3 4 0
I want to count the number of rows with out=(0, 2, or 4) and later display rows where flag = 1
Basically:
select id, count(where out IN(0,2,4)) as cnt where flag = 1
My desired output:
id cnt
--- ----
1 2
2 3
3 3
This query works as expected but only when I don't have "where flag = 1":
select id,sum(case when out in (0,2,4) then 1 else 0 end) over(partition by id) as cnt
from tablename
Is there a way for me to calculate sum() of something first and store it in a column, and later filter out rows using where clause? currently what's happening is that rows are being filtered out first and later sum(...) is calculated.
Any way I can rectify this?

You can use a case expression to sum only rows for an id when they have specified values for the out column.
select id,sum(case when out in (0,2,4) then 1 else 0 end) as cnt
from tablename
group by id
Edit: To include other columns when summing, use the sum window function.
select * from (select id,flag
,sum(case when out in (0,2,4) then 1 else 0 end) over(partition by id) as cnt
--include other columns as required
from tablename
) x
where flag=1

select id,count(*) as cnt
from tablename
where out in (0,2,4)
group by id

Do count for 0,2,4 OUT. And then filter them with a Inner Join or using Correlated subquery
Schema:
CREATE TABLE #TAB (ID INT, OUT_VALUE INT, FLAG INT)
INSERT INTO #TAB
SELECT 1, 0, 1
UNION ALL
SELECT 1, 1, 0
UNION ALL
SELECT 1, 4, 0
UNION ALL
SELECT 2, 0, 1
UNION ALL
SELECT 2, 2, 0
UNION ALL
SELECT 2, 2, 0
UNION ALL
SELECT 3, 0, 1
UNION ALL
SELECT 3, 2, 0
UNION ALL
SELECT 3, 1, 0
UNION ALL
SELECT 3, 4, 0
UNION ALL
SELECT 4, 4, 0
Now do select like below
INNER JOIN
SELECT T1.ID,COUNT(1) C
FROM #TAB T1
INNER JOIN
(
SELECT DISTINCT ID FROM #TAB WHERE FLAG = 1
)FLAGS ON T1.ID = FLAGS.ID
WHERE T1.OUT_VALUE IN (0,2,4)
GROUP BY T1.ID
Correlated Sub-query
SELECT ID, C FROM (
SELECT T1.ID
,COUNT(1) C
,(
SELECT DISTINCT ID
FROM #TAB T2
WHERE T1.ID = T2.ID
AND T2.FLAG = 1
) FLAG
FROM #TAB T1 WHERE T1.OUT_VALUE IN (0,2,4)
GROUP BY T1.ID
)A
WHERE A.FLAG IS NOT NULL
The result will be
+----+---+
| ID | C |
+----+---+
| 1 | 2 |
| 2 | 3 |
| 3 | 3 |
+----+---+

Related

Need to get the mismatcted cells in specific partition

ID
TC_No
Result
1
tc_1
PASS
1
tc_2
PASS
1
tc_3
FAIL
1
tc_4
PASS
1
tc_5
FAIL
2
tc_1
FAIL
2
tc_2
PASS
2
tc_3
FAIL
2
tc_4
FAIL
2
tc_5
FAIL
I'm trying to find all records that have conflicting "Result" on the same "TC_No" and among different "ID" values, filtered by ID IN (1,2).
Here's the expected output:
ID
TC_No
Result
1
tc_1
PASS
1
tc_4
PASS
2
tc_1
FAIL
2
tc_4
FAIL
and my attempted query:
SELECT * From
(SELECT * from Excel As T1
UNION
SELECT * from Excel As T2)
As c
where ID in(1,2) order By TC_NO
find the distinct count of result for each tc_no and then select the records having count greater than 1
Query
select * from your_tbl_name a
where exists(
select 1 from (
select tc_no, count(distinct result) as cnt
from your_tbl_name
where result in ('PASS','FAIL')
group by c_no
) b
where a.tc_no = b.tc_no
and b.cnt > 1
)
You can simply INNER JOIN the table onto itself and add a predicate in the WHERE clause to return only mismatched results.
SQL:
SELECT
a.ID,
a.TC_No,
a.Result
FROM
Excel a
INNER JOIN Excel b ON a.TC_No = b.TC_No
WHERE
a.Result <> b.Result;
Result:
| ID | TC_No | Result |
|----|-------|--------|
| 1 | tc_1 | PASS |
| 1 | tc_4 | PASS |
| 2 | tc_1 | FAIL |
| 2 | tc_4 | FAIL |
SQL Fiddle Demo:
Here
Check when the maximum result is different than the minimum one, in your filtered data, using window functions.
WITH cte AS (
SELECT tab.*,
MAX(Result_) OVER(PARTITION BY TC_No) AS max_result,
MIN(Result_) OVER(PARTITION BY TC_No) AS min_result
FROM tab
WHERE ID IN (1,2)
)
SELECT Id, Tc_No, Result_
FROM cte
WHERE min_result < max_result
Check the demo here.
You could use analytic function COUNT() OVER() to find the rows with different content and then just filter the result in Where clause:
SELECT ID, TC_NO, RESULT
FROM ( Select ID, TC_NO, RESULT,
CASE WHEN Count(DISTINCT RESULT) OVER(Partition By TC_NO) = 2 THEN 'Y' END "IS_DIFF"
From tbl
)
WHERE IS_DIFF = 'Y'
ORDER BY ID
With your sample data:
WITH
tbl (ID, TC_NO, RESULT) AS
(
Select 1, 'tc_1', 'PASS' From Dual Union All
Select 1, 'tc_2', 'PASS' From Dual Union All
Select 1, 'tc_3', 'FAIL' From Dual Union All
Select 1, 'tc_4', 'PASS' From Dual Union All
Select 1, 'tc_5', 'FAIL' From Dual Union All
Select 2, 'tc_1', 'FAIL' From Dual Union All
Select 2, 'tc_2', 'PASS' From Dual Union All
Select 2, 'tc_3', 'FAIL' From Dual Union All
Select 2, 'tc_4', 'FAIL' From Dual Union All
Select 2, 'tc_5', 'FAIL' From Dual
)
... the result is:
ID
TC_NO
RESULT
1
tc_1
PASS
1
tc_4
PASS
2
tc_1
FAIL
2
tc_4
FAIL

SQL theory: Filtering out duplicates in one column, picking lowest value in other column

I am trying to figure out the best way to remove rows from a result set where either the value in one column or the value in a different column has a duplicate in the result set.
Imagine the results of a query are as follows:
a_value | b_value
-----------------
1 | 1
2 | 1
2 | 2
3 | 1
4 | 3
5 | 2
6 | 4
6 | 5
What I want to do is:
Eliminate all rows that have duplicate values in a_value
Pick only 1 row for a given b_value
So I'd want the filtered results to end up like this after eliminating a_value duplicates:
a_value | b_value
-----------------
1 | 1
3 | 1
4 | 3
5 | 2
And then like this after picking only a single b_value:
a_value | b_value
-----------------
1 | 1
4 | 3
5 | 2
I'd appreciate suggestions on how to accomplish this task in an efficient way via SQL.
with
q_res ( a_value, b_value ) as (
select 1, 1 from dual union all
select 2, 1 from dual union all
select 2, 2 from dual union all
select 3, 1 from dual union all
select 4, 3 from dual union all
select 5, 2 from dual union all
select 6, 4 from dual union all
select 6, 5 from dual
)
-- end test data; solution begins below
select min(a_value) as a_value, b_value
from (
select a_value, min(b_value) as b_value
from q_res
group by a_value
having count(*) = 1
)
group by b_value
order by a_value -- ORDER BY is optional
;
A_VALUE B_VALUE
------- -------
1 1
4 3
5 2
1) In the inner query I am avoiding all duplicates which are present in a_value
column and getting all the remaining rows from input table and storing them
as t2. By joining t2 with t1 there would be full data without any dups as per
your #1 in requirement.
SELECT t1.*
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value;
2) Once the filtered data is obtained, I am assigning rank to each row in the filtered dataset obtained in step-1 and I am selecting only rows with rank=1.
SELECT X.a_value,
X.b_value
FROM
(
SELECT t1.*,
ROW_NUMBER() OVER ( PARTITION BY t1.b_value ORDER BY t1.a_value,t1.b_value ) AS rn
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value
) X
WHERE X.rn = 1;

get a new table from two tables based on the columns' values in a table in SQL server:

I need to get a new table from two tables in SQL server:
tbl1:
id value
1 abc
2 abd
3 dft
tbl2:
num abc abd dft
1 5 9 0
2 8 0 7
if a column is not 0 in tbl2, get id from tbl1 like:
num id
1 1 (abc <> 0 in tbl2)
1 2 (abd <> 0 in tbl2)
2 1 (abc <> 0 in tbl2)
2 3 (dft <> 0 in tbl2)
My SQL query :
select tbl2.num, case when (tbl2.abc != 0 ) then select tbl1.id from tbl1 end as id
from tbl2, tbl1
But, it is not what I want. I do not want to use loop to do it.
Thanks!
I think you want union all:
select num, 1 as id from tbl2 where abc <> 0
union all
select num, 2 as id from tbl2 where abd <> 0
union all
select num, 3 as id from tbl2 where dft <> 0;
If you want to pick up the ids from the first table:
select t2.num, t1.id
from tbl2 cross join
(select id from tbl1 where value = 'abc') t1
where abc <> 0
union all
select num, t1.id
from tbl2 cross join
(select id from tbl1 where value = 'abd') t1
where abd <> 0
union all
select num, t1.id
from tbl2 cross join
(select id from tbl1 where value = 'dft') t1
where dft <> 0;
You can use unpivot like this:
;WITH tbl1 AS (
SELECT * FROM (VALUES
(1, 'abc'),
(2, 'abd'),
(3, 'dft')) as t1(id, value)
)
,tbl2 AS (
SELECT * FROM (VALUES
(1, 5, 9, 0),
(2, 8, 0, 7)) as t2(num, abc, abd, dft)
)
--Unpivot the table.
SELECT num, tbl1.id
FROM
(SELECT num,
CASE WHEN abc != 0 THEN 1 ELSE NULL END as abc,
CASE WHEN abd != 0 THEN 1 ELSE NULL END as abd,
CASE WHEN dft != 0 THEN 1 ELSE NULL END as dft
FROM tbl2) p
UNPIVOT
(value FOR id IN
(abc, abd, dft)
)AS unpvt
LEFT JOIN tbl1 on tbl1.value = unpvt.id
The result:
num id
----------- -----------
1 1
1 2
2 1
2 3

SQL - Group by to get sum but also return a row if the sum is 0

I have the following table:
ID BuyOrSell Total
4 B 10
4 B 11
4 S 13
4 S 29
8 B 20
9 S 23
What I am trying to do is to have sum of B and S columns for each ID and if there is not a B or S for an ID have a row with 0, so expected output would be
ID BuyOrSell Total
4 B 21
4 S 42
8 B 20
8 S 0
9 S 23
9 B 0
I have tried this and it is kind of doing what I am after but not exactly:
DECLARE #Temp Table (ID int, BuyOrSell VARCHAR(1), charge Decimal)
INSERT INTO #Temp
SELECT 4, 'B', 10 UNION ALL
SELECT 4, 'B', 11 UNION ALL
SELECT 4, 'S', 13 UNION ALL
SELECT 4, 'S', 29 UNION ALL
SELECT 8, 'B', 20 UNION ALL
SELECT 9, 'S', 23
;With Results AS
(
SELECT ID,
BuyOrSell,
SUM(charge) AS TOTAL
FROM #Temp
Group by ID, BuyOrSell
)
Select t.*,max(
case when BuyOrSell = 'B' then 'Bfound'
end) over (partition by ID) as ref
,max(
case when BuyOrSell = 'S' then 'Sfound'
end) over (partition by ID) as ref
FROM Results t;
Thanks
Try this:
;WITH CTE(ID, BuyOrSell) AS(
SELECT
ID, T.BuyOrSell
FROM #Temp
CROSS JOIN(
SELECT 'B' UNION ALL SELECT 'S'
)T(BuyOrSell)
GROUP BY ID, T.BuyOrSell
)
SELECT
C.ID,
C.BuyOrSell,
Total = ISNULL(SUM(T.charge), 0)
FROM CTE C
LEFT JOIN #Temp T
ON T.ID = C.ID
AND T.BuyOrSell = C.BuyOrSell
GROUP BY C.ID, C.BuyOrSell
ORDER BY C.ID, C.BuyOrSell
#03Usr, despite that your question has been answered, please try this:
SELECT two.ID,
two.BuyOrSell,
ISNULL (one.Total, 0) Total
FROM
(SELECT ID,
BuyOrSell,
SUM (Total) Total
FROM #Temp
GROUP BY ID, BuyOrSell) one
LEFT OUTER JOIN
(SELECT ID,
BuyOrSell
FROM #Temp
GROUP BY ID,
BuyOrSell) two
ON one.ID = two.ID
AND one.BuyOrSell = two.BuyOrSell;
Here is a solution with tricky join:
SELECT t1.ID,
v.l as BuyOrSell,
SUM(CASE WHEN t1.BuyOrSell = v.l THEN t1.charge ELSE 0 END) AS Total
FROM #Temp t1
JOIN (VALUES('B'),('S')) v(l)
ON t1.BuyOrSell = CASE WHEN EXISTS(SELECT * FROM #Temp t2
WHERE t2.ID = t1.ID AND t2.BuyOrSell <> t1.BuyOrSell)
THEN v.l ELSE t1.BuyOrSell END
GROUP BY t1.ID, v.l
ORDER BY t1.ID, v.l
Output:
ID l Total
4 B 21
4 S 42
8 B 20
8 S 0
9 B 0
9 S 23

SQL to check for all values in column

I have the following table in Oracle DB.
ID VALUE
-----------
1 1
1 2
1 3
2 1
2 2
3 1
3 2
3 3
4 1
How can I select ID's which have all 3 values (1,2,3)
The simplest option is generally something like this
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 val from dual union all
3 select 1 id, 2 val from dual union all
4 select 1 id, 3 val from dual union all
5 select 2 id, 1 val from dual union all
6 select 2 id, 2 val from dual union all
7 select 3 id, 1 val from dual union all
8 select 3 id, 2 val from dual union all
9 select 3 id, 3 val from dual union all
10 select 4 id, 1 val from dual
11 )
12 select id
13 from x
14 where val in (1,2,3)
15 group by id
16* having count(distinct val) = 3
SQL> /
ID
----------
1
3
The WHERE clause identifies the values you're interested in. The HAVING clause tells you how many of those values need to exist. If you wanted all the rows that had at least 2 of the 3 values, for example, you'd change the HAVING clause to look for a COUNT of 2.
If a particular val is guaranteed to occur at most once per id, you can eliminate the distinct in the HAVING clause.
Try this:
SELECT ID
FROM TABLENAME T
WHERE EXISTS (SELECT *
FROM TABLENAME T1
WHERE T1.ID = T.ID AND T1.VALUE = '1')
AND EXISTS (SELECT *
FROM TABLENAME T2
WHERE T1.ID = T.ID AND T2.VALUE = '2')
AND EXISTS (SELECT *
FROM TABLENAME T3
WHERE T1.ID = T.ID AND T2.VALUE = '3')
or
SELECT ID
FROM TABLENAME T
WHERE (SELECT COUNT( * )
FROM (SELECT VALUE
FROM TABLENAME T1
WHERE T1.ID = T.ID
GROUP BY VALUE)) = 3;
where 3 is number of values which can be calculated by a
SELECT COUNT( * )
FROM TABLENAME T1
GROUP BY VALUE
so this will be general purpose:
SELECT ID
FROM TABLENAME T
WHERE (SELECT COUNT( * )
FROM (SELECT VALUE
FROM TABLENAME T1
WHERE T1.ID = T.ID
GROUP BY VALUE)) = (SELECT COUNT( * )
FROM TABLENAME T2
GROUP BY VALUE)
Here's an option... each expression in the HAVING clause is counting the number of values that are found equal to 1, 2, or 3. If any of these counts is less than 1, then the ID will not be returned.
http://sqlfiddle.com/#!4/00fdc/8
SELECT ID
FROM myTable
GROUP BY ID
HAVING
SUM(DECODE(VALUE, 1, 1, 0)) > 0 AND
SUM(DECODE(VALUE, 2, 1, 0)) > 0 AND
SUM(DECODE(VALUE, 3, 1, 0)) > 0
EDIT - To require value 1, and either 2 or 3:
SELECT ID
FROM myTable
GROUP BY ID
HAVING
SUM(DECODE(VALUE, 1, 1, 0)) > 0 AND
(
SUM(DECODE(VALUE, 2, 1, 0)) > 0 OR
SUM(DECODE(VALUE, 3, 1, 0)) > 0
)
select id from (select id,sum(case when value=1 then 1 else 0 end) as 'v1',
sum(case when value=2 then 1 else 0 end) as 'v2',
sum(case when value=3 then 1 else 0 end) as 'v3'
from orac group by id) as final
where v1>0 and v2>0 and v3>0
With this option you will get more than the IDs, up to your application to select the column you want:
SELECT ID,
sum(CASE WHEN VALUE = 1 THEN 1 ELSE 0 END) AS ONE,
sum(CASE WHEN VALUE = 2 THEN 1 ELSE 0 END) AS TWO,
sum(CASE WHEN VALUE = 3 THEN 1 ELSE 0 END) AS THREE
FROM MYTABLE
GROUP BY ID
HAVING ONE >= 1 AND TWO >= 1 AND THREE >= 1;
alternatively if your case is specific (only values 1, 2, 3 are possible, and no duplicate values are allowed), then you could try the following one:
SELECT ID,
count(VALUE) AS VALUECOUNT
FROM MYTABLE
GROUP BY ID
HAVING VALUECOUNT = 3;
I would take care before going that way, as you might get side effects if later you want to add additional values. But it's still worth proposing if your current case fits the restrictions given above.
And, of course, if you don't like the idea of fetching these intermediate counts, enclose the queries I gave within another select
SELECT ID FROM (
...
)