Computing multiple averages in a table with multiple different conditions - sql-server-2012

I'm trying to compute average for multiple columns in my table and I'm trying to impose rules to calculate this average based on a where clause. Since I do not have distinct values on most of these columns, I am unable to do a group by clause to get the correct results. A simple where clause with all the conditions does not compute the average correctly. Is there a workaround for this problem?
My table looks like
col1 col2 col3 col4
0 -3 0 109
3 2 -1 -108
-4 -1 3 3
2 0 4 2
2 1 107 -2
My intention here is to compute avg for each column based on a rule. For ex:avg(col1) should include values between 0 and 100, avg(col2) should include values between -1 to -100, avg(col3) should include values between 0 and 100,
avg(col4) should include values between -1 to -100.
select avg(col1), avg(col2), avg(col3), avg(col4) from tbl1
where (col1 between 0 and 100) and (col1 between -1 and -100) and (col3 between 0 and 100) and (col4 between -1 and -100)

The following solution should accomplish your objectives. First, we use some CTE (subqueries) to just limit the values in the columns to what we want, then we get their averages.
WITH
first_col AS
(
SELECT col1
FROM the_table
WHERE col1 >= 0 and col1 <= 100
),
second_col AS
(
SELECT col2
FROM the_table
WHERE col2 >= -100 and col2 <= -1
),
third_col AS
(
SELECT col3
FROM the_table
WHERE col3 >= 0 and col3 <= 100
),
fourth_col AS
(
SELECT col4
FROM the_table
WHERE col4 >= -100 and col4 <= -1
)
SELECT
AVG(first_col.col1),
AVG(second_col.col2),
AVG(third_col.col3),
AVG(fourth_col.col4)
FROM
first_col,
second_col,
third_col,
fourth_col
You can look at this SQL FIDDLE for the result.
1 -2 2 -2
Alternatively, you can use this query to avoid using CTE
SELECT
AVG(CASE WHEN col1 >= 0 AND col1 <= 100 THEN col1 END) AS col1avg,
AVG(CASE WHEN col2 >= -100 AND col2 <= -1 THEN col2 END) AS col2avg,
AVG(CASE WHEN col3 >= 0 AND col3 <= 100 THEN col3 END) AS col3avg,
AVG(CASE WHEN col4 >= -100 AND col4 <= -1 THEN col4 END) AS col4avg
FROM
the_table
And you can see the results on this SQL FIDDLE
1 -2 2 -2
Note - This does assume you want the values inclusive, since the BETWEEN statement is inclusive.

Related

Sort columns in a row after numeric value

I have a task where I need to order the results of query in a way, that the highest of 3 values gets displayed in the first column after the ID, the second highest in the middle one and the smallest in the last column.
The tables look like this:
ID
Col1
Col2
Col3
1234
30
50
40
2345
40
30
60
3456
60
50
40
And the result should look like this:
ID
Col1
Col2
Col3
1234
50
40
30
2345
60
40
30
3456
60
50
40
The values are package dimensions which need to be in order to be processed.
Thank you in advance :)
This is pretty simple using apply:
select t.*, v.*
from t outer apply
(select max(case when seqnum = 1 then col end) as col1,
max(case when seqnum = 2 then col end) as col2,
max(case when seqnum = 3 then col end) as col3
from (select v.col,
row_number() over (order by v.col desc) as seqnum
from (values (t.col1), (t.col2), (t.col3)
) v(col)
) v
) v;
SQL Server is quite efficient when using APPLY within a single row. I would expect the performance to be comparable to a bunch of complex case expressions. In addition, this gives more flexibility if any of the values are NULL.
And, it is much easier to expand to more columns!
Unfortunately, there is no simple and short way of achieving this.
Try below query:
select
case when col1 > col2 and col1 > col3 then col1
else case when col2 > col1 and col2 > col3 then col2
else col3 end end,
case when (col1 > col2 and col1 < col3) or (col1 < col2 and col1 > col3) then col1
else case when (col2 > col1 and col2 < col3) or (col2 < col1 and col2 > col3) then col2
else col3 end end,
case when col1 < col2 and col1 < col3 then col1
else case when col2 < col1 and col2 < col3 then col2
else col3 end end
from tbl
SQL fiddle
You may also unpivot it, sort the value and then pivot it back
SELECT *
FROM
(
SELECT t.id, v.col,
col_no = row_number() over (partition by t.id order by v.col desc)
FROM yourtable t
CROSS APPLY
(
VALUES (col1), (col2), (col3)
) v (col)
) d
PIVOT
(
MAX(col)
for col_no in ([1], [2], [3])
) p
the question is not clear but you can use a temp table. First, read the top 3 of data with ordering highest, secondly do it same again with offset value 3, 6 and third times order by lowest.
push the results after each step
read the temp table

Sqlite insert both even and odd rows in one expression

I am using sqlite3 and I have a sqlite table which has somewhat duplicated/overlapping columns. To illustrate:
No Col1 Col2 Col3 Col4
row1 1 1 1 2 2
row2 2 1 1 3 3
row3 3 2 2 4 4
row4 4 2 2 5 5
Col1 and Col2 stores the same information, however, Col3 and Col4 has different information.
I want to condense the rows into one row like this:
No Col1 Col2 Col3 Col4 Col3.2 Col4.2
row1 1 1 1 2 2 3 3
row3 3 2 2 4 4 5 5
I have created a new table with the columns, and was able to select the odd rows.
INSERT INTO [Table] ( No, Col1, Col2, Col3, Col4
)
SELECT No, Col1, Col2, Col3, Col4
FROM [Table]
WHERE ([No] % 2) = 1
ORDER BY [No];
The result table would be something like:
No Col1 Col2 Col3 Col4 Col3.2 Col4.2
row1 1 1 1 2 2 null null
row3 3 2 2 4 4 null null
Now I am not sure how to insert the even values into the new table. Using similar expressions only insert more rows. Is it possible to do this INSERT INTO expression in one sentence? Or how do I update the new table?
Just join the table with itself based on the following condition. It'll even work if the No column has gaps:
SELECT o.No, o.Col1, o.Col2, o.Col3, o.Col4, e.Col3, e.Col4
FROM t AS o
INNER JOIN t AS e ON o.Col1 = e.Col1
AND o.Col2 = e.Col2
AND o.No < e.No
Use pivoting logic with aggregation:
SELECT
MIN(No) AS No,
MAX(CASE WHEN No % 2 = 1 THEN Col1 END) AS Col1,
MAX(CASE WHEN No % 2 = 1 THEN Col2 END) AS Col2,
MAX(CASE WHEN No % 2 = 1 THEN Col3 END) AS Col3,
MAX(CASE WHEN No % 2 = 1 THEN Col4 END) AS Col4,
MAX(CASE WHEN No % 2 = 0 THEN Col1 END) AS Col1_2,
MAX(CASE WHEN No % 2 = 0 THEN Col2 END) AS Col2_2,
MAX(CASE WHEN No % 2 = 0 THEN Col3 END) AS Col3_2,
MAX(CASE WHEN No % 2 = 0 THEN Col4 END) AS Col4_2
FROM yourTable
GROUP BY
(No-1) / 2;
Demo
Another approach, using window functions added in sqlite 3.25:
CREATE TABLE table2(no INTEGER PRIMARY KEY, col1, col2, col3, col4, "col3.2", "col4.2");
INSERT INTO table2
SELECT *
FROM (SELECT no, col1, col2, col3, col4, lead(col3) OVER win, lead(col4) OVER win
FROM table1
WINDOW win AS (ORDER BY no))
WHERE no % 2 = 1;
which gives
SELECT * FROM table2;
no col1 col2 col3 col4 col3.2 col4.2
---------- ---------- ---------- ---------- ---------- ---------- ----------
1 1 1 2 2 3 3
3 2 2 4 4 5 5

Count per category

have a table as below -
COL1 | COL2 | COL3
1 1 1
1 1 2
1 2 0
1 2 1
2 3 1
2 3 2
2 4 0
2 4 1
3 1 0
3 2 0
.
.
.
I want to select COL1 where all COL2 have sum(COL3) is > 0. If I am sure there are 20 distinct values in COL2, Then how can i pull all COL1 values that have all 20 COL2 filled with COL3 > 0. So the end result should be
COL1 | COL2 | COL3
1 1 3
1 2 1
2 3 3
2 4 1
I have tried a lot of ways to do this but no success.
Just use group by and having.
select col1,col2,sum(col3)
from tbl
group by col1,col2
having sum(col3)>0
select t1.*
from yourTable t1
inner join
(
select t.col1
from
(
select col1, col2, sum(col3) as col_sum
from yourTable
group by col1, col2
) t
group by t.col1
having sum(case when t.col_sum = 0 then 1 else 0 end) = 0
) t2
on t1.col1 = t2.col1
I use a CTE and a Group by with a where condition
;WITH CTE as (
select COL1,COL2,SUM(COL3) as COL3 FROM table1
Group By
COL1,COL2
)
select * from CTE
where COL3>0
Just group col2 and check if it's bigger then 0
select col1,col2,sum(col3)
from tbl
group by col2
having sum(col3)>0
http://sqlfiddle.com/#!9/537f8c/1
See if the below gives you the result that you are after. It is selecting the col1, col2 and a sum of col3 from a derived(?) table that is excluding the col3's that are 0:
select col1, col2, sum(col3)
from
(
select col1, col2, col3 from tbl where col3 <> 0
) as ds
group by col3

selection based on certain condition

select col1, col2, col3 from tab1
rownum col1 col2 col3
1 1 10 A
2 1 15 B
3 1 0 A
4 1 0 C
5 2 0 B
6 3 20 C
7 3 0 D
8 4 10 B
9 5 0 A
10 5 0 B
Output required is
col1 col2 col3
1 10 A
1 15 B
2 0 B
3 20 C
4 10 B
5 0 A
5 0 B
col1 and col2 are my lookup/joining columns columns, if col2 is having "non zero" data then I need to ignore/filter record with 0 (in above example I need to filter record rownum 3 4 and 7) If col2 is not having any data other than "non zero" in that case only select record with 0 (in above example col1 with value 1 and 5).
I m trying to write sql for this. Hope I have mentioned requirement clearly, please let me know if you need anything more from my side. Seem to have gone blank in this case.
Database - Oracle 10g
SELECT col1,
col2,
col3
FROM (SELECT col1,
col2,
col3,
sum(col2) OVER (PARTITION BY col1) sum_col2
FROM tab1)
WHERE ( ( sum_col2 <> 0
AND col2 <> 0)
OR sum_col2 = 0)
If col2 can be negative and the requirement is that the sum of col2 has "non-zero" data then the above is OK, however, if it is the requirement that any col2 value has "non-zero" data then it should be changed to:
SELECT col1,
col2,
col3
FROM (SELECT col1,
col2,
col3,
sum(abs(col2)) OVER (PARTITION BY col1) sum_col2
FROM tab1)
WHERE ( ( sum_col2 <> 0
AND col2 <> 0)
OR sum_col2 = 0)
SELECT t1.*
FROM tab1 t1
JOIN (SELECT "col1", MAX("col2") AS max2
FROM tab1
GROUP BY "col1") t2
ON t1."col1" = t2."col1"
WHERE ((max2 = 0 AND "col2" = 0)
OR
(max2 != 0 AND "col2" != 0))
ORDER BY "rownum"
DEMO

Element-wise quotient of two columns in SQL

How can I combine the columns returned by two SELECT statements to give their element-wise quotient?
Query 1:
SELECT COUNT(*) AS count
FROM table1
WHERE col2 = 1 AND col3 > 5
GROUP BY col4
ORDER BY col4
Query 2:
SELECT COUNT(*) AS count
FROM table1
WHERE col2 = 1
GROUP BY col4
ORDER BY col4
So if they return something like:
Query 1 Query 2
count count
-----------------------
1 5
2 4
I will get:
quotient
-------
0.2
0.5
With the 4-column version of the question, we can assume that the quotient is between groups with the same value in col4. So, the answer becomes:
SELECT col4, SUM(CASE WHEN col3 > 5 THEN 1 ELSE 0 END) / COUNT(*) AS quotient
FROM table1
WHERE col2 = 1
GROUP BY col4;
I've retained col4 in the output because I don't think the ratios (quotients) will be useful without something to identify which quotient is associated with which values, though theoretically, the answer doesn't want that column in the output.
In this case, you don't need two separate queries at all:
SELECT SUM(col3 > 5) / COUNT(*)
FROM table1
WHERE col2 = 1
GROUP BY col4
ORDER BY col4
In case your actual queries cannot be simplified as per the other answers, you can join the subqueries, like this:
select j1.count / j2.count as quotient
from (
SELECT col4, COUNT(*) AS count
FROM table1
WHERE col2 = 1 AND col3 > 5
GROUP BY col4
) j1
join (
SELECT col4, COUNT(*) AS count
FROM table1
WHERE col2 = 1
GROUP BY col4
) j2 on j1.col4=j2.col4