Different values of column 1 based on column 2 SQL Server - sql

I have a table with columns ID and Val. For each value of ID we can have either same or different values of Val.
ID Val
1 A
1 NULL
2 00
2 00
2 00
2 00
3 00
3 A
4 A
5 00
5 00
5 A
6 A
6 A
6 NULL
6 00
From above table, I am looking for IDs which has different values in Val column. If for any given ID all values of Val column are same then it should not come in result.
So result would be something like.
D Val
1 A
1 NULL
3 00
3 A
5 00
5 00
5 A
6 A
6 A
6 NULL
6 00
Id 2 should not come in result because for Id 2, Val column has same data.
Similarly ID 4 will not come in result as ID 4 has only one row.
For each ID if we have more than one value in Val column then is it should show in result.
Thanks for the Help!

For the ids that meet the condition of having different values:
select id
from t
group by id
having min(id) <> max(id);
You can then incorporate this into a query as:
select t.*
from t join
(select id
from t
group by id
having min(id) <> max(id)
) tt
on t.id = tt.id;
Or, you can use window functions:
select t.id, t.val
from (select t.*,
min(val) over (partition by id) as minval,
max(val) over (partition by id) as maxval
from t
) t
where minval <> maxval;

Try this:
SELECT ID, Val
FROM mytable
WHERE ID IN (SELECT ID
FROM mytable
GROUP BY ID
HAVING COUNT(DISTINCT CASE
WHEN Val IS NULL THEN ''
ELSE Val
END) > 1
I've made the assumption that Val field is of type VARCHAR and that it can be either NULL or <> ''.

Build your query at three steps:
Select distinct values id, val (to ensure you will get the null safe count)
Count distinct values for each id
Show results from source table
Try to use inner select instead of subselect to speed up the query.
The solution is written in the query below:
SELECT
t.*
FROM
-- select only ids with distinct count > 1
(
SELECT
id
FROM
-- select distinct values to ensure your count of null values is real
(
SELECT DISTINCT
id, val
FROM
t
) AS td
GROUP BY
id
HAVING
COUNT(*) > 1
) AS tc
-- join the source table
INNER JOIN
t
ON
t.id = tc.id

Related

How do i select all columns, plus the result of the sum

I have this select:
"Select * from table" that return:
Id
Value
1
1
1
1
2
10
2
10
My goal is create a sum from each Value group by id like this:
Id
Value
Sum
1
1
2
1
1
2
2
10
20
2
10
20
I Have tried ways like:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
But the is not grouping by id.
Id
Value
Sum
1
1
1
1
1
1
2
10
10
2
10
10
Aggregation aggregates rows, reducing the number of records in the output. In this case you want to apply the result of a computation to each of your records, task carried out by the corresponding window function.
SELECT table.*, SUM(Value) OVER(PARTITION BY Id) AS sum_
FROM table
Check the demo here.
Your attempt looks correct.
Can you try the below query :
It works for me :
SELECT Id, Value,
(SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY ID) as sum
FROM Table v;
You can do it using inner join to join with selection grouped by id :
select t.*, sum
from _table t
inner join (
select id, sum(Value) as sum
from _table
group by id
) as s on s.id = t.id
You can check it here
Your select is ok if you adjust it just a little:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
GROUP BY IDRNC is a mistake and should be GROUP BY ID
you should give an alias to a sum column ...
subquery selecting the sum does not have to have self table alias to be compared with outer query that has one (this is not a mistake - works either way)
Test:
WITH
a_table (ID, VALUE) AS
(
Select 1, 1 From Dual Union All
Select 1, 1 From Dual Union All
Select 2, 10 From Dual Union All
Select 2, 10 From Dual
)
SELECT ID, VALUE, (SELECT SUM(VALUE) FROM a_table WHERE ID = v.ID GROUP BY ID) "ID_SUM" FROM a_table v;
ID VALUE ID_SUM
---------- ---------- ----------
1 1 2
1 1 2
2 10 20
2 10 20

Selecting nth top row based on number of occurrences of value in 3 tables

I have three tables let's say A, B and C. Each of them has column that's named differently, let's say D1, D2 and D3. In those columns I have values between 1 and 26. How do I count occurrences of those values and sort them by that count?
Example:
TableA.D1
1
2
1
1
3
TableB.D2
2
1
1
1
2
3
TableC.D3
2
1
3
So the output for 3rd most common value would look like this:
3 -- number 3 appeared only 3 times
Likewise, output for 2nd most common value would be:
2 -- number 2 appeared 4 times
And output for 1st most common value:
1 -- number 1 appeared 7 times
You probably want :
select top (3) d1
from ((select d1 from tablea ta) union all
(select d2 from tableb tb) union all
(select d3 from tablec tc)
) t
group by d1
order by count(*) desc;
SELECT DQ3.X, DQ3.CNT
(
SELECT DQ2.*, dense_rank() OVER (ORDER BY DQ2.CNT DESC) AS RN
(SELECT DS.X,COUNT(DS.X) CNT FROM
(select D1 as X FROM TableA UNION ALL SELECT D2 AS X FROM TABLE2 UNION ALL SELECT D3 AS X FROM TABLE3) AS DS
GROUP BY DS.X
) DQ2
) DQ3 WHERE DQ3.RN = 3 --the third in the order of commonness - note that 'ties' can be handled differently
One of the things about SQL scripts: they get difficult to read very easily. I'm a big fan of making things as readable as absolute possible. So I'd recommend something like:
declare #topThree TABLE(entry int, cnt int)
select TOP 3 entry,count(*) as cnt
from
(
select d1 as entry from tablea UNION ALL
select d2 as entry from tableb UNION ALL
select d3 as entry from tablec UNION ALL
) as allTablesCombinedSubquery
order by count(*)
select TOP 1 entry
from #topThree
order by cnt desc
... it's extremely readable, and doesn't use any concepts that are tough to grok.

how to add column value in next colum

id value
1 10
2 20
3 30
4 40
5 50
Required output
table name data.
id value
1 10 //( 10+0(previous value))
2 30 //( 20+10(previous value))
3 50 //( 30+20(previous value))
4 70 //( 40+30(previous value))
5 90 //(50+40(previous value))
please provide sql query
You are looking for LAG which is standard SQL and should be available in later DB2 versions if I'm not mistaken.
select
id,
value + coalesce( lag(value) over (order by id), 0 ) as value
from mytable
order by id;
In case LAG OVER is not available, SUM OVER may be:
select
id,
coalesce( sum(value) over (order by id rows between 1 preceding and current row), 0 )
as value
from mytable
order by id;
solution 1:
select f1.id,
ifnull((select f2.value from yourtable f2 where f1.id - 1 =f2.id), 0) + f1.value as value
from yourtable f1
solution 2:
select f1.id,
ifnull(f3.value, 0) + f1.value as value
from yourtable f1
left outer join lateral
(
select f2.value from yourtable f2
where f1.id - 1 =f2.id
) f3 on 1=1

Use a standard single sql to group data (SQL Server)

Raw data with 2 columns:
0 33
2 null
0 44
2 null
2 null
2 null
0 55
2 null
2 null
.....
Results I want:
2 33
2 44
2 44
2 44
2 55
2 55
....
Can I use a SQL statement to accomplish this? (return the rows with 2 only but fill with values come from the previous row that is 0), there could be many '2 null' between 0.
This way
with s as (
select *
from
(values
(1,0,33 ),
(2,2,null),
(3,0,44 ),
(4,2,null),
(5,2,null),
(6,2,null),
(7,0,55 ),
(8,2,null),
(9,2,null)
) T(id,a,b)
)
select s1.a, t.b
from s s1
cross apply (
select top(1) s2.b
from s s2
where s2.id < s1.id and s2.b is not null and s2.a = 0
order by s2.id desc ) t
where s1.a = 2
order by s1.id;
I use CROSS APPLY so the query may be easily extended to get other columns from the relevant '0' row.
First of all, select value for every row with null:
SELECT col2 FROM (SELECT MAX(ID) FROM your_tbl t WHERE t.ID < ID AND col2 IS NOT NULL);
Then write a condition for your table with that subquery:
SELECT col1, (
SELECT col2 FROM your_tbl where id = (SELECT MAX(ID) FROM your_tbl t
WHERE t.ID < tbl.ID AND col2 IS NOT NULL))
FROM your_tbl tbl WHERE col1 <> 0;

SELECT records until new value SQL

I have a table
Val | Number
08 | 1
09 | 1
10 | 1
11 | 3
12 | 0
13 | 1
14 | 1
15 | 1
I need to return the last values where Number = 1 (however many that may be) until Number changes, but do not need the first instances where Number = 1. Essentially I need to select back until Number changes to 0 (15, 14, 13)
Is there a proper way to do this in MSSQL?
Based on following:
I need to return the last values where Number = 1
Essentially I need to select back until Number changes to 0 (15, 14,
13)
Try (Fiddle demo ):
select val, number
from T
where val > (select max(val)
from T
where number<>1)
EDIT: to address all possible combinations (Fiddle demo 2)
;with cte1 as
(
select 1 id, max(val) maxOne
from T
where number=1
),
cte2 as
(
select 1 id, isnull(max(val),0) maxOther
from T
where val < (select maxOne from cte1) and number<>1
)
select val, number
from T cross join
(select maxOne, maxOther
from cte1 join cte2 on cte1.id = cte2.id
) X
where val>maxOther and val<=maxOne
I think you can use window functions, something like this:
with cte as (
-- generate two row_number to enumerate distinct groups
select
Val, Number,
row_number() over(partition by Number order by Val) as rn1,
row_number() over(order by Val) as rn2
from Table1
), cte2 as (
-- get groups with Number = 1 and last group
select
Val, Number,
rn2 - rn1 as rn1, max(rn2 - rn1) over() as rn2
from cte
where Number = 1
)
select Val, Number
from cte2
where rn1 = rn2
sql fiddle demo
DEMO: http://sqlfiddle.com/#!3/e7d54/23
DDL
create table T(val int identity(8,1), number int)
insert into T values
(1),(1),(1),(3),(0),(1),(1),(1),(0),(2)
DML
; WITH last_1 AS (
SELECT Max(val) As val
FROM t
WHERE number = 1
)
, last_non_1 AS (
SELECT Coalesce(Max(val), -937) As val
FROM t
WHERE EXISTS (
SELECT val
FROM last_1
WHERE last_1.val > t.val
)
AND number <> 1
)
SELECT t.val
, t.number
FROM t
CROSS
JOIN last_1
CROSS
JOIN last_non_1
WHERE t.val <= last_1.val
AND t.val > last_non_1.val
I know it's a little verbose but I've deliberately kept it that way to illustrate the methodolgy.
Find the highest val where number=1.
For all values where the val is less than the number found in step 1, find the largest val where the number<>1
Finally, find the rows that fall within the values we uncovered in steps 1 & 2.
select val, count (number) from
yourtable
group by val
having count(number) > 1
The having clause is the key here, giving you all the vals that have more than one value of 1.
This is a common approach for getting rows until some value changes. For your specific case use desc in proper spots.
Create sample table
select * into #tmp from
(select 1 as id, 'Alpha' as value union all
select 2 as id, 'Alpha' as value union all
select 3 as id, 'Alpha' as value union all
select 4 as id, 'Beta' as value union all
select 5 as id, 'Alpha' as value union all
select 6 as id, 'Gamma' as value union all
select 7 as id, 'Alpha' as value) t
Pull top rows until value changes:
with cte as (select * from #tmp t)
select * from
(select cte.*, ROW_NUMBER() over (order by id) rn from cte) OriginTable
inner join
(
select cte.*, ROW_NUMBER() over (order by id) rn from cte
where cte.value = (select top 1 cte.value from cte order by cte.id)
) OnlyFirstValueRecords
on OriginTable.rn = OnlyFirstValueRecords.rn and OriginTable.id = OnlyFirstValueRecords.id
On the left side we put an original table. On the right side we put only rows whose value is equal to the value in first line.
Records in both tables will be same until target value changes. After line #3 row numbers will get different IDs associated because of the offset and will never be joined with original table:
LEFT RIGHT
ID Value RN ID Value RN
1 Alpha 1 | 1 Alpha 1
2 Alpha 2 | 2 Alpha 2
3 Alpha 3 | 3 Alpha 3
----------------------- result set ends here
4 Beta 4 | 5 Alpha 4
5 Alpha 5 | 7 Alpha 5
6 Gamma 6 |
7 Alpha 7 |
The ID must be unique. Ordering by this ID must be same in both ROW_NUMBER() functions.