How to check If table contains diferent values? - sql

I have table:
Id Value
1 79868
2 79868
3 79868
4 97889
5 97889
Now, I want to make next select with bool variable that check if table contains difrent values at table column Value. Something like this:
select
v= (select case when exists(...)
then 1
else 0
end)
Table contais Values: 79868, 97889 so v should return 1 in other case 0.
How to write select iniside select case??

You can compare the min and max values:
select (case when (select min(value) from t) = (select max(value) from t)
then 1 else 0
end) as all_same
With an index on (value), this should be quite fast.
The above solution assumes that there are no null values or that NULL values should be ignored.

You might try this:
SELECT CASE COUNT(*)
WHEN 1 THEN 1
ELSE 0
END AS all_equal
FROM (SELECT DISTINCT Value FROM my_table);

If I get your question correct, you want to check if value column contains more than 1 distinct values. You can achieve this using,
select (case when count(value) > 1 then 1 else 0 end) as out
from (select value from table group by value) temp

May this is better:
SELECT CASE COUNT(DISTINCT value) WHEN 1 THEN 1
ELSE 0
END AS all_equal
FROM my_table;

So, you just need one case expression with two Boolean variable
declare #bit1 bit = 1, #bit0 bit = 0
select
(case when min(value) = max(value) then #bit1 else #bit0 end) as v
from table t
where value is not null

This is a the same as another answers
But is has some test data
declare #T table(pk int identity primary key, val int not null);
insert into #T (val) values (79868), (79868), (79868);
select case when count(distinct(val)) = 1 then 0 else 1 end as dd
from #t t;
select case when min(val) = max(val) then 0 else 1 end as dd
from #t t;
insert into #T (val) values (97889), (97889);
select case when count(distinct(val)) = 1 then 0 else 1 end as dd
from #t t;
select case when min(val) = max(val) then 0 else 1 end as dd
from #t t;
I like the min max answer from Gordon best

Related

How can I change the type of output?

My sample query is:
SELECT
SUM(CASE WHEN tb.[CashStatus] = 0 AND tb.[CashPayMethod] = 0 THEN tb.[CashPayPrice] ELSE 0 END) AS [TotalCashPrice],
SUM(CASE WHEN tb.[CashStatus] = 0 AND tb.[CashPayMethod] = 10 THEN tb.[CashPayPrice] ELSE 0 END) AS [TotalPOSPrice]
FROM
mytable tb
The result is:
TotalCashPrice | TotalPOsPrice
---------------+----------------
41,000,000 | 12,000,000
I want to change it to:
Value | Name
-----------------+--------------------
41,000,000 | TotalCashPrice
12,000,000 | TotalPOSPrice
Thanks everyone.
I would use GROUP BY and move the CASE expression to the column being aggregated:
SELECT (CASE WHEN tb.CashPayMethod = 0 THEN 'TotalCashPrice'
WHEN tb.CashPayMethod = 10 THEN 'TotalPOSPrice'
END) as name,
SUM(tb.CashPayPrice) as value
FROM mytable tb
WHERE tb.CashStatus = 0 AND tb.CashPayMethod IN (0, 10)
GROUP BY tb.[CashPayMethod]
One simple option here is to just take a union:
SELECT SUM(CashPayPrice) AS Value, 'TotalCashPrice' AS Name
FROM mytable
WHERE CashStatus = 0 AND CashPayMethod = 0
UNION ALL
SELECT SUM(CashPayPrice), 'TotalPOSPrice'
FROM mytable
WHERE CashStatus = 0 AND CashPayMethod = 10;
Another option would be to look into using SQL Server's UNPIVOT operator. But that might be overkill if your actual problem isn't much bigger than this.
You can use UNPIVOT to achieve desired output:
declare #tmp table(TotalCashPrice int, TotalPOsPrice int)
insert into #tmp values (41000000, 12000000)
select u.[Value], u.[Name]
from #tmp s
unpivot
(
[Value]
for [Name] in ([TotalCashPrice], [TotalPOsPrice])
) u
Result:
If the original column names (TotalCashPrice, TotalPOsPrice) can change in number you will need dynamic TSQL.

SQL Server - Get column who have specific value

I have a SQL query which returns :
id | value
1 a
1 a
1 b
2 a
2 a
I want to get only id who have only the value a. So the id 2
How to do this ?
You can use aggregation and having clause to check if all the rows have value 'a' for a given id:
Using Count:
select id
from t
group by id
having count(*) = count(case when value = 'a' then 1 end);
Or using Sum
select id
from t
group by id
having SUM(case when value = 'a' then 0 else 1 end) = 0;
Use the next code:-
Select id
from #test
group by id
having sum (case when value = 'a' then 0 else 1 end) = 0
The clue is passing 0 for 'a' and pass 1 for other, then having sum equals 0
This is slightly slower than #Gurwinder Singh's answer but can be more readable if performance is not your top priority.
CREATE TABLE tmp (id int, [value] char(1))
INSERT INTO tmp values (1,'a'),(1,'a'),(1,'b'),(2,'a'),(2,'a')
SELECT DISTINCT id
FROM tmp a
WHERE [value] = 'a'
AND id NOT IN (
SELECT id FROM tmp
WHERE [value] <> 'a')

How best to Count(*) with a CASE STATEMENT?

The following SQL (on SQL Server) returns an error of:
Incorrect syntax near '*'
Is there something inherently wrong with using the following SELECT statement?:
SELECT
COUNT(CASE WHEN <conditions> THEN * ELSE NULL END) as conditionalcountall
FROM TABLE
I tried this variation which also failed:
SELECT
CASE WHEN <conditions> THEN COUNT(*) ELSE NULL END as conditionalcountall
FROM TABLE
I tend to like sum()
SELECT
SUM(CASE WHEN <conditions> THEN 1 ELSE 0 END) as conditionalcountall
FROM TABLE
Try This, it is Tested
SELECT
CASE WHEN 1 = 1 THEN COUNT(*) ELSE NULL END as conditionalcountall
FROM TABLE
1 = 1is example conditions
Demo:-
Create table #temp (id int , col1 varchar (10))
go
insert into #temp values (1 , 'aaaa')
insert into #temp values (2 , 'bbbb')
insert into #temp values (3 , 'cccc')
SELECT
CASE WHEN 1 = 1 THEN COUNT(*) ELSE NULL END as conditionalcountall
FROM #temp
Result:
In Case Condation like that id = 1 you should select Count(*) in CASE cluse in your query
like this:
SELECT
CASE WHEN id = 1 THEN (select COUNT(*) from #temp) ELSE NULL END as conditionalcountall
FROM #temp
Result:-
Note: if You used Count(*) directly, you counted the id column, so you should use group by as next:
SELECT
CASE WHEN id = 1 THEN COUNT(*) ELSE NULL END as conditionalcountall
FROM #temp
group by id
Result:
SELECT
CASE WHEN X THEN Y
ELSE Z
END *NEW COLUMN NAME*
, COUNT(*)
FROM
TABLE
GROUP BY
*NEW COLUMN NAME*
This should return two columns, one with the buckets/case statement and one with the count of the columns for each one of your buckets
This method was the most straightforward for myself
If you REALLY, REALLY want to use COUNT, then you can do this:
SELECT
COUNT(*)
FROM table
WHERE <conditions>

sql select statement select sum where

I have two temp tables (below), the first marks one of five conditions. The second pulls from that table and does a sum and a count based on the condition. How could I get the second table to work with this, or a similar format?
select ID
,sum_value
,condition_field
,'condition_1' = case when condition_type in (1,2) then 1 else 0 end
,'condition_2' = case when condition_type in (3,4) then 1 else 0 end
--etc...
into #temp
from my_table
select ID
,sum_value
,'1_amt' = SUM(sum_value) from #temp where condition_1 = 1
,'1_cnt' = COUNT(ID) from #temp where condition_1 = 1
,'2_amt' = SUM(sum_value) from #temp where condition_2 = 1
,'2_cnt' = COUNT(ID) form #temp where condition_2 = 2
from #temp
You want something more like this:
SUM(CASE WHEN condition_1=1 THEN sum_value ELSE 0 END) AS 1_amt

Looping in select query

I want to do something like this:
select id,
count(*) as total,
FOR temp IN SELECT DISTINCT somerow FROM mytable ORDER BY somerow LOOP
sum(case when somerow = temp then 1 else 0 end) temp,
END LOOP;
from mytable
group by id
order by id
I created working select:
select id,
count(*) as total,
sum(case when somerow = 'a' then 1 else 0 end) somerow_a,
sum(case when somerow = 'b' then 1 else 0 end) somerow_b,
sum(case when somerow = 'c' then 1 else 0 end) somerow_c,
sum(case when somerow = 'd' then 1 else 0 end) somerow_d,
sum(case when somerow = 'e' then 1 else 0 end) somerow_e,
sum(case when somerow = 'f' then 1 else 0 end) somerow_f,
sum(case when somerow = 'g' then 1 else 0 end) somerow_g,
sum(case when somerow = 'h' then 1 else 0 end) somerow_h,
sum(case when somerow = 'i' then 1 else 0 end) somerow_i,
sum(case when somerow = 'j' then 1 else 0 end) somerow_j,
sum(case when somerow = 'k' then 1 else 0 end) somerow_k
from mytable
group by id
order by id
this works, but it is 'static' - if some new value will be added to 'somerow' I will have to change sql manually to get all the values from somerow column, and that is why I'm wondering if it is possible to do something with for loop.
So what I want to get is this:
id somerow_a somerow_b ....
0 3 2 ....
1 2 10 ....
2 19 3 ....
. ... ...
. ... ...
. ... ...
So what I'd like to do is to count all the rows which has some specific letter in it and group it by id (this id isn't primary key, but it is repeating - for id there are about 80 different values possible).
http://sqlfiddle.com/#!15/18feb/2
Are arrays good for you? (SQL Fiddle)
select
id,
sum(totalcol) as total,
array_agg(somecol) as somecol,
array_agg(totalcol) as totalcol
from (
select id, somecol, count(*) as totalcol
from mytable
group by id, somecol
) s
group by id
;
id | total | somecol | totalcol
----+-------+---------+----------
1 | 6 | {b,a,c} | {2,1,3}
2 | 5 | {d,f} | {2,3}
In 9.2 it is possible to have a set of JSON objects (Fiddle)
select row_to_json(s)
from (
select
id,
sum(totalcol) as total,
array_agg(somecol) as somecol,
array_agg(totalcol) as totalcol
from (
select id, somecol, count(*) as totalcol
from mytable
group by id, somecol
) s
group by id
) s
;
row_to_json
---------------------------------------------------------------
{"id":1,"total":6,"somecol":["b","a","c"],"totalcol":[2,1,3]}
{"id":2,"total":5,"somecol":["d","f"],"totalcol":[2,3]}
In 9.3, with the addition of lateral, a single object (Fiddle)
select to_json(format('{%s}', (string_agg(j, ','))))
from (
select format('%s:%s', to_json(id), to_json(c)) as j
from
(
select
id,
sum(totalcol) as total_sum,
array_agg(somecol) as somecol_array,
array_agg(totalcol) as totalcol_array
from (
select id, somecol, count(*) as totalcol
from mytable
group by id, somecol
) s
group by id
) s
cross join lateral
(
select
total_sum as total,
somecol_array as somecol,
totalcol_array as totalcol
) c
) s
;
to_json
---------------------------------------------------------------------------------------------------------------------------------------
"{1:{\"total\":6,\"somecol\":[\"b\",\"a\",\"c\"],\"totalcol\":[2,1,3]},2:{\"total\":5,\"somecol\":[\"d\",\"f\"],\"totalcol\":[2,3]}}"
In 9.2 it is also possible to have a single object in a more convoluted way using subqueries in instead of lateral
SQL is very rigid about the return type. It demands to know what to return beforehand.
For a completely dynamic number of resulting values, you can only use arrays like #Clodoaldo posted. Effectively a static return type, you do not get individual columns for each value.
If you know the number of columns at call time ("semi-dynamic"), you can create a function taking (and returning) polymorphic parameters. Closely related answer with lots of details:
Dynamic alternative to pivot with CASE and GROUP BY
(You also find a related answer with arrays from #Clodoaldo there.)
Your remaining option is to use two round-trips to the server. The first to determine the the actual query with the actual return type. The second to execute the query based on the first call.
Else, you have to go with a static query. While doing that, I see two nicer options for what you have right now:
1. Simpler expression
select id
, count(*) AS total
, count(somecol = 'a' OR NULL) AS somerow_a
, count(somecol = 'b' OR NULL) AS somerow_b
, ...
from mytable
group by id
order by id;
How does it work?
Compute percents from SUM() in the same SELECT sql query
SQL Fiddle.
2. crosstab()
crosstab() is more complex at first, but written in C, optimized for the task and shorter for long lists. You need the additional module tablefunc installed. Read the basics here if you are not familiar:
PostgreSQL Crosstab Query
SELECT * FROM crosstab(
$$
SELECT id
, count(*) OVER (PARTITION BY id)::int AS total
, somecol
, count(*)::int AS ct -- casting to int, don't think you need bigint?
FROM mytable
GROUP BY 1,3
ORDER BY 1,3
$$
,
$$SELECT unnest('{a,b,c,d}'::text[])$$
) AS f (id int, total int, a int, b int, c int, d int);