Average across rows in SQL Server 2008 - sql

I have the following table
Data Data1 Data2 YTD
-------------------------
1 2 3
2 3 4
3 3 6
In the YTD column I have to average the rows data. I can use average in columns but not sure how to average across rows.
Looking for the below results and using SQL Server 2008
Data Data1 Data2 YTD
---------------------------------
1 2 3 2 (Average)
2 3 4 3
3 null 6 4.5

I think cross apply is the simplest method:
select t.*, v.avg_data
from t cross apply
(select avg(v.data) as avg_data
from (values (t.data), (t.data1), (t.data2)) v(data)
) v;
Use case expressions, you can also express this as:
select t.*,
( (coalesce(t.data, 0) + (t.data1, 0) + coalesce(t.data2, 0)) /
nullif( (case when t.data is not null then 1 else 0 end) +
(case when t.dat1 is not null then 1 else 0 end) +
(case when t.dat2 is not null then 1 else 0 end), 0
)
) as avg_data;
However, this formulation is messy and prone to typing errors.

Related

How to find the mode of 3 columns in SQL?

In SQL I have a table with 3 columns:
Month1
Month2
Month3
0
1
0
1
1
1
0
1
1
...and so on.
I need another column where it gives the mode of Month1, Month2 and Month3.
My expected output is:
Month1
Month2
Month3
Mode
0
1
0
0
1
1
1
1
0
1
1
1
So far I have only calculated mode for a single column. Not sure how we can do it horizontally by combining 3 columns.
This should work, can easily be expanded for n columns:
select month1, month2, month3, ca.val
from t
cross apply (
select top 1 val
from (values
(month1),
(month2),
(month3)
) as v(val)
group by val
order by count(*) desc
) as ca
For RDBMS other than SQL server, replace values(...) with appropriate table value constructor, cross apply with lateral join/sub query inside select and top 1 with limit/offset...fetch.
You could use a CASE expression:
SELECT *, CASE WHEN Month1 + Month2 + Month3 <= 1 THEN 0 ELSE 1 END AS mode
FROM yourTable;

Counting values within the same row in SQL Server

My SQL Server problem is as follows: let's say we have clients and we wish to rate them based on 4 categories (Score_1...Score_4) on a scale of 0 to 2. I have a table presented below:
What I want my code to do is count the number of 0, 1, and 2 values each of my clients recieved. The result table I would like to get would like this:
So client_1 got two 0 scores, one 1 score and one 2 score, client_2 got one 0 score, one 1 score and two 2 scores. Any suggestions? Thanks in advance!
You can unpivot, then do conditional aggregation:
select t.id,
sum(case when x.score = 0 then 1 else 0 end) cnt_0,
sum(case when x.score = 1 then 1 else 0 end) cnt_1,
sum(case when x.score = 2 then 1 else 0 end) cnt_2
from mytable t
cross apply (values (score_1), (score_2), (score_3), (score_4)) x(score)
group by t.id

SQL query count (recursive)

I have the following table on my database which contains some transactions for which I need to calc points and rewards.
Every time a TxType A occurs I should record 10 points.
Then I have to subtract from these points the value of the PP column every time a TxType B occurs.
When the calculation goes to zero a reward is reached.
ID TxType PP
1 A 0
2 B 2
3 B 1
4 B 1
5 B 1
6 B 3
7 B 1
8 B 1
9 A 0
10 B 4
11 B 3
12 B 2
13 B 1
14 A 0
15 B 2
I have created the sql query to calc points as follow
SELECT SUM(
CASE
WHEN TxType = 'A' THEN 10
WHEN TxType = 'B' THEN (PP * -1)
END)
FROM myTable
This query return the value of 8, which is exactly the number of points based on the sample data.
How do I calculate the rewards occurred (2 in the given example)?
thanks for helping
One way to do the calculation (in SQL Server 2008) using a correlated subquery:
select t.*,
(select sum(case when TxType = 'A' then 10
when TxType = 'B' then PP * -1
end)
from mytable t2
where t2.id <= t.id
) as TheSum
from mytable t;
You can then apply the logic of what happens when the value is 0. In SQL Server 2012, you could just use a cumulative sum.
To complete Gordon Linoff's the answer, you just need to count the records where TheSum is 0 to get how many rewards occurred:
SELECT COUNT(1)
FROM (
SELECT ID,
TxType,
PP,
( SELECT SUM(CASE TxType WHEN 'A' THEN 10 WHEN 'B' THEN -PP END)
FROM #myTable t2
WHERE t2.id <= t1.id
) AS TheSum
FROM #myTable t1
) Result
WHERE TheSum = 0

sql query different column based on input

I am using MS-SQL 2008. I have a table with different columns based on locations in it that will have a 'Y' or Null value. The table also has other data other than location from survey results. I have set up a temptable #TempLocation to hold the location based on the one or all. I need to select rows from the table based on 'Y' from one or more location rows within a date range.
TableID Northwest Northeast Southwest Southeast Batchno first_choice date_completed
1 Y Y Y 1 A 2012-11-10
2 Y Y 1 SA 2012-19-10
3 Y Y 1 N 2012-07-10
4 Y Y Y 2 A 2012-10-10
5 Y 2 A 2012-16-10
6 Y Y 2 D 2012-21-10
7 Y NULL A 2012-19-10
8 Y Y Y Y 3 SA 2012-11-10
9 Y 3 A 2012-10-10
10 Y Y 3 A 2012-07-10
I have created a Dynamic SQL statement to pull one location successfully but is it possible to pull all of them?
select ''' + (SELECT * FROM #TempLocation) + ''',
count(batchno),
count(case when first_choice is not null then batchno end),
count(case when t.First_choice =''SD'' then 1 end) ,
count(case when t.First_choice=''D'' then 1 end) ,
count(case when t.First_choice=''N'' then 1 end) ,
count(case when t.First_choice=''A'' then 1 end) ,
count(case when t.First_choice=''SA'' then 1 end)
from customer_satisfaction_survey t
where t.date_completed>= ''' + CAST(#beg_date AS VARCHAR) + '''
and t.date_completed < ''' + CAST(dateadd(day,1,#end_date) AS Varchar) + '''
and t.' + (SELECT * FROM #TempLocation) + ' = ''Y'''
An All result would look like this.
Number Location Total Total2 SA A N D SD
1 Northwest 6 6 1 3 1 1 0
2 Northeast 5 4 2 2 1 0 0
3 Southwest 4 4 1 3 0 0 0
4 Southeast 6 6 2 3 0 1 0
I have to think that you are approaching this in the wrong way, because your data is not normalized. The first thing you should do is to normalize the data using UNPIVOT. I'm assuming that you are using SQL Server, since your syntax suggests that. It is a good idea to tag all questions with the database, though.
You can unpivot your data with a statement such as:
select BatchNo, FirstChoice, DateCompleted, Location
from d
unpivot (val for location in (Northwest, Northeast, Southwest, Southeast)) as unpvt
Next, set up your temporary table to have a separate row for each location. Then, you can do the join with no dynamic SQL. Something like:
with dnorm as (
THE NORMALIZATION QUERY HERE
)
select dnorm.location, count(*) as total,
sum(case when dnorm.first_choice is not null then 1 else 0 end) as total2,
sum(case when dnorm.first_choice = 'SA' then 1 else 0 end) as SA,
. . .
from dnorm join
#TempLocation tl
on dnorm.location = tl.location
where ALL YOUR WHERE CONDITIONS HERE
The final query looks something like:
with dnorm as (
select BatchNo, FirstChoice, DateCompleted, Location
from d
unpivot (val for location in (Northwest, Northeast, Southwest, Southeast)) as unpvt
)
select dnorm.location, count(*) as total,
sum(case when dnorm.first_choice is not null then 1 else 0 end) as total2,
sum(case when dnorm.first_choice = 'SA' then 1 else 0 end) as SA,
. . .
from dnorm join
#TempLocation tl
on dnorm.location = tl.location
where ALL YOUR WHERE CONDITIONS HERE
The dynamic SQL approach is quite clever, but I don't think it is the simplest way to approach this.

SQL Level the data conditionally

I have the following puzzle to solve (an urgent business assignment to be exact)
SQL SERVER 2008
I have a table of this form
ID Market SubMarket Value
1 1 1 3
2 1 2 6
3 1 3 2
4 2 23 1
5 2 24 9
I have specific MarketIDs and every MarketID has specific SubMarketIDs (maximum 5 - I know how may for each)
eg MarketID 1 has SubMarketIDs 1,2,3
MarketID 2 has SubMarketIDs 23,24 etc
and each SubMarketID has a variable value
I must transform my data in a fixed table of this type
MarketID SubMarketAvalue SubMarketBValue SubMarketCValue....SubMarketEValue
1 3 6 2 null
2 1 9 null null
SubMarketAValue must contain the value of the smaller SubMarketID
SubMarketBValue must contain the value of the next bigger SubMarketID
You did not specify the RDBMS, but you can use the following in SQL Server 2005+, Oracle and PostgreSQL:
select market,
max(case when rn = 1 then value end) as SubMarketAvalue,
max(case when rn = 2 then value end) as SubMarketBvalue,
max(case when rn = 3 then value end) as SubMarketCvalue,
max(case when rn = 4 then value end) as SubMarketDvalue,
max(case when rn = 5 then value end) as SubMarketEvalue
from
(
select id, market, submarket, value,
row_number() over(partition by market
order by market, submarket) rn
from yourtable
) x
group by market
see SQL Fiddle with Demo