Getting average from 3 columns in SQL Server - sql

I have a table with 3 columns (smallint) in SQL Server 2005.
Table Ratings
ratin1 smallint,
ratin2 smallint
ratin3 smallint
These columns can have values from 0 to 5.
How can I select the average value of these fields, but only compare fields where the value is greater then 0.
So if the column values are 1, 3 ,5 - the average has to be 3.
if the values are 0, 3, 5 - The average has to be 4.

This is kind of quick and dirty, but it will work...
SELECT (ratin1 + ratin2 + ratin3) /
((CASE WHEN ratin1 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin2 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin3 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin1 = 0 AND ratin2 = 0 AND ratin3 = 0 THEN 1 ELSE 0 END) AS Average

#mwigdahl - this breaks if any of the values are NULL. Use the NVL (value, default) to avoid this:
Sum columns with null values in oracle
Edit: This only works in Oracle. In TSQL, try encapsulating each field with an ISNULL() statement.

There should be an aggregate average function for sql server.
http://msdn.microsoft.com/en-us/library/ms177677.aspx

This is trickier than it looks, but you can do this:
SELECT dbo.MyAvg(ratin1, ratin2, ratin3) from TableRatings
If you create this function first:
CREATE FUNCTION [dbo].[MyAvg]
(
#a int,
#b int,
#c int
)
RETURNS int
AS
BEGIN
DECLARE #result int
DECLARE #divisor int
SELECT #divisor = 3
IF #a = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #b = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #c = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #divisor = 0
SELECT #result = 0
ELSE
SELECT #result = (#a + #b + #c) / #divisor
RETURN #Result
END

select
(
select avg(v)
from (values (Ratin1), (Ratin2), (Ratin3)) as value(v)
) as average

You can use the AVG() function. This will get the average for a column. So, you could nest a SELECT statement with the AVG() methods and then SELECT these values.
Pseudo:
SELECT col1, col2, col3
FROM (
SELECT AVG(col1) AS col1, AVG(col2) AS col2, AVG(col3) AS col3
FROM table
) as tbl
WHERE col1 IN (0, 3, 5)
etc.

Related

A SQL query to count how many attributes a record has in common with another record?

I am trying to write a SQL query to take the count of columns with equal value in my schema for each row by comparison to a single record.
Example:
record1: 1, 0, 1, 0, 0
record2: 0, 0, 0, 0, 1
record3: 0, 0, 1, 0, 0
record1 has 2 attributes in common with record2, go through the entire table and order by number of attributes each record has in common with record1
Is there a way to write a SQL statement that will do this? I have only found ways to compare each row and specify which attributes must be of equal value.
You can do:
select t.*,
((case when t.col1 = t1.col1 then 1 else 0 end) +
(case when t.col2 = t1.col2 then 1 else 0 end) +
(case when t.col3 = t1.col3 then 1 else 0 end) +
. . .
) as num_in_common
from t cross join
t t1
where t1.id = 1; -- or however you define "record1"
order by num_in_common desc;
Here's a nice routine you can use in SQL Server that will do this if you'd like. Replace #temp with your table name:
declare #holding table (id int, col1 int, col2 int, col3 int, col4 int, col5 int, num_in_common int)
declare #iterator int = 1
declare #row1col1 int, #row1col2 int, #row1col3 int, #row1col4 int ,#row1col5 int
while #iterator<=(select max(id) from #temp)
begin
if #iterator=1
select #row1col1=col1, #row1col2=col2, #row1col3=col3, #row1col4=col4 ,#row1col5=col5
from #temp where id=#iterator
else
insert #holding
select *, case when col1-#row1col1 = 0 then 1 else 0 end +
case when col2-#row1col2 = 0 then 1 else 0 end +
case when col3-#row1col3 = 0 then 1 else 0 end +
case when col4-#row1col4 = 0 then 1 else 0 end +
case when col5-#row1col5 = 0 then 1 else 0 end
from #temp where id=#iterator
set #iterator=#iterator+1
end
select * from #holding

Performing a sub query alternatives

I'm looking for advice on how to get around the
"Cannot perform an aggregate function on an expression containing an
aggregate or a subquery".
On the Select PlateID From #Instances code, in the example below. Does anyone have any ideas or suggestions?
DECLARE #Instances AS TABLE(PlateID INT);
INSERT INTO #Instances(PlateID)VALUES(11638),(11637),(11632),(11659)
DECLARE #NumberofPlates INT;
SELECT #NumberofPlates = COUNT(*) FROM #Instances;
SELECT Instance_Plate_Room_Instance_ID_LNK
from dbo.M_Instance_Plate
WHERE Instance_Plate_Deleted = 0
group by Instance_Plate_Room_Instance_ID_LNK
having sum(case
when Instance_Plate_Plate_ID_LNK not in (SELECT PlateID
FROM #Instances)
then 1 else 0 end) = 0 and
SUM(case
when Instance_Plate_Plate_ID_LNK in (SELECT PlateID
FROM #Instances)
then 1 else 0 end) = #NumberofPlates;
In the absence of the structure of the physical table you included in your query I mocked up some random data for this, and put together a query that seems to work?
DECLARE #Instances AS TABLE(PlateID INT);
INSERT INTO #Instances(PlateID) VALUES (11638),(11637),(11632),(11632);
DECLARE #M_Instance_Plate TABLE (Instance_Plate_Plate_ID_LNK INT, Instance_Plate_Deleted INT, Instance_Plate_Room_Instance_ID_LNK INT);
INSERT INTO #M_Instance_Plate SELECT 11638, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11637, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11632, 0, 100;
INSERT INTO #M_Instance_Plate SELECT 11632, 0, 200;
INSERT INTO #M_Instance_Plate SELECT 11632, 1, 300;
DECLARE #NumberofPlates INT;
SELECT #NumberofPlates = COUNT(*) FROM #Instances;
WITH x AS (
SELECT
Instance_Plate_Room_Instance_ID_LNK,
SUM(CASE WHEN Instance_Plate_Plate_ID_LNK IS NULL THEN 1 ELSE 0 END) AS test_1, --Any missing
SUM(CASE WHEN Instance_Plate_Plate_ID_LNK IS NOT NULL THEN 1 ELSE 0 END) AS test_2 --Has coverage
FROM
#M_Instance_Plate ip
LEFT JOIN #Instances i ON i.PlateID = ip.Instance_Plate_Plate_ID_LNK
WHERE
Instance_Plate_Deleted = 0
GROUP BY
Instance_Plate_Room_Instance_ID_LNK)
SELECT
Instance_Plate_Room_Instance_ID_LNK
FROM
x
WHERE
test_1 = 0
AND test_2 = #NumberofPlates;
INSERT INTO #Instances(PlateID)VALUES(11638),(11637),(11632),(11659)
--DECLARE #NumberofPlates INT;
--SELECT #NumberofPlates = COUNT(*) FROM #Instances;
SELECT Instance_Plate_Room_Instance_ID_LNK
FROM dbo.M_Instance_Plate p
WHERE Instance_Plate_Deleted = 0
AND NOT EXISTS (
SELECT 1
FROM #Instances i
LEFT JOIN M_Instance_Plate m ON i.PlateID = m.Instance_Plate_Plate_ID_LNK
WHERE m.Instance_Plate_Room_Instance_ID_LNK = p.Instance_Plate_Room_Instance_ID_LNK
AND m.Instance_Plate_Plate_ID_LNK IS NULL
UNION ALL
SELECT 1
FROM #Instances i
JOIN M_Instance_Plate m ON i.PlateID = m.Instance_Plate_Plate_ID_LNK
WHERE m.Instance_Plate_Room_Instance_ID_LNK = p.Instance_Plate_Room_Instance_ID_LNK
GROUP BY i.PlateID
HAVING COUNT(*) != 1
)
Try this instead. Is equivalent expression (checks if plateId exists on your table variable and then matches with your variable #numberofplates)
HAVING #NumberofPlates = (
SELECT COUNT(1) AS cc
FROM #Instances AS a
WHERE a.PlateID = Instance_Plate_Plate_ID_LNK
)

SQL Replace an empty SQL SELECT with word

I'm trying to solve the following problem:
I would like to make a select, when the result is empty it should be replaced with 'empty'
Else the result should be there.
That is my try:
select case (count*)
when 0 then 'empty'
ELSE
THEVALUEOFTHECOLUM
END AS RESULT
from Database.table where CarID = 12;
Thanks for every comment.
This should work, but you might have to convert the second occurrence of COUNT(*) to VARCHAR depending on the database used:
SELECT
CASE WHEN COUNT(*) = 0
THEN 'empty'
ELSE COUNT(*) -- CONVERT, TO_CHAR, ...
END AS result
FROM Database.table where CarID = 12;
SELECT
CASE
WHEN Q.countvalue = 0 THEN 'Empty'
ELSE CONVERT(NVARCHAR(10), Q.countvalue)
END AS RESULT
FROM
(
SELECT COUNT(*) AS countvalue
FROM Database.table WHERE CarID = 12
) AS Q
This feels hacky to me, but it will return the column data.
It is not one query, but it's still setwise.
declare #tmp table (id int)
declare #cnt int
insert into #tmp select col from table
select #cnt = count(*) from #tmp
if(#cnt = 0)
begin
select 'empty'
end
else
begin
select * from #tmp
end
Is it possible to code it with one query?
If there are no results -> no result found
else
Show all results, not only one
declare #tmp table (id int)
declare #cnt int
insert into #tmp select col from table
select #cnt = count(*) from #tmp
if(#cnt = 0)
begin
select 'empty'
end
else
begin
select * from #tmp
end

How do I check whether data is there or not in more than one sql table in one script?

I have more than 3 sql tables.
now i'm trying to select count(*) from all tables but how can i do this?.
I want to know whether data is present in all tables or not
I need to check the row count from previous business day ~15% for any table and it sends an email alert
I tried like following please help me to complete
PROCEDURE [dbo].[SendEmail_WSOTableDataAlert]
AS
BEGIN
declare #err int
IF NOT EXISTS (select 1 from T1) OR
NOT EXISTS (select 1 from T2)
BEGIN
set #error=1
END
//here i need to show which table is having empty data how can i do this please help
SET #tableHTML = #tableHTML + +
'</TABLE>' + #EmailFooter;
#error =1
then
send mail
END
Select
case when count(*) = 0 then
'No rows'
else
'Has rows'
end
FROM
(
Select * from #table1
UNION ALL
Select * from #table2
UNION ALL
Select * from #table3
) t
UPDATE
This makes sure all of then have at least one row and fail if any of them does not have record
Select
case when count(*) = 0 then
'No rows'
else
'Has rows'
end
FROM
(
Select top 1 1 found from #table1
intersect
Select top 1 1 found from #table2
intersect
Select top 1 1 found from #table3
) t
You can try multiplying the flags indicating zero counts together. If any of them is zero, the result will be zero.
select (case when (select count(*) from table1)=0 then 0 else 1 end
*case when (select count(*) from table2)=0 then 0 else 1 end
*case when (select count(*) from table3)=0 then 0 else 1 end) as no_zeros
If you would like to know which table has all zeros, you could transform the query as follows:
select (case when (select count(*) from table1)=0 then 1 else 0 end
+case when (select count(*) from table2)=0 then 2 else 0 end
+case when (select count(*) from table3)=0 then 4 else 0 end
+case when (select count(*) from table4)=0 then 8 else 0 end) as no_zeros
Use powers of two (1, 2, 4, 8, 16, 32, and so on) as your flags. Ones 1 in the binary representation of the result will tell you which tables have no records.
(select count() from table1 )
union all
(select count() from table2 )
union all
(select count(*) from table3 )
And then loop through the rows of the result
declare #count1 int
select #count1 = count(*)
from table1
declare #count2 int
select #count2 = count(*)
from table2
declare #count3 int
select #count3 = count(*)
from table3
if (#count1 + #count2 + #count3 = 0)
--do something
else
--do something else
You can use the EXISTS keyword to efficiently check if there is any data in a table.
IF NOT EXISTS (SELECT 1 FROM Table1) OR NOT EXISTS (SELECT 1 FROM Table2) OR NOT EXISTS (SELECT 1 FROM Table3)
BEGIN
/* do something */
END

SQL function, extract numbers before -

i'm playing around with building a sql function that will extract numbers from a title, which is what the following code below does. Although, i want to modify this function to parse numbers into sections. For example:
Current Data in title field:
QW 1 RT 309-23-1
QW 1 RT 29-1
QW 1 RT 750-1
QW RT 750-1
Temp tables created once function is ran on title field:
column 1 Column 2 Column 3 Column 4
1 309 23 1
1 29 1 Null
1 750 1 Null
Null 750 1 Null
create function [dbo].[ExtractNumbers](#Numbers nvarchar(2000))
returns nvarchar(2000)
as
BEGIN
declare #NonNumericIndex int
set #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
WHILE #NonNumericIndex > 0
begin
SET #Numbers = REPLACE(#Numbers,SUBSTRING(#Numbers,#NonNumericIndex,1),'')
SET #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
SET
end
return #Numbers
END
Here's one way.
Although actually at the end I realised the format was more fixed than I had originally realised so you may be better off just using the various string manipulation functions to calculate the columns directly.
WITH TestTable AS
(
SELECT 'QW 1 RT 309-23-1' AS title UNION ALL
SELECT 'QW 1 RT 29-1' UNION ALL
SELECT 'QW 1 RT 750-1' UNION ALL
SELECT 'QW RT 750-1'
)
SELECT title, [1] AS [Column 1], [2] AS [Column 2],[3] AS [Column 3],[4] AS [Column 4]
FROM TestTable CROSS APPLY dbo.GetNumbers(title)
PIVOT
(MAX(num) FOR idx IN ([1], [2],[3],[4])
) AS PivotTable;
Uses the following TVF
CREATE FUNCTION GetNumbers
(
#Numbers NVARCHAR(2000)
)
RETURNS #Results TABLE
(
idx INT IDENTITY(1,1),
num INT
)
AS
BEGIN
DECLARE #NonNumericIndex INT, #NumericIndex INT
SET #NumericIndex = PATINDEX('%[0-9]%',#Numbers)
IF (#NumericIndex > 4) --First Column not there
INSERT INTO #Results VALUES (NULL)
WHILE #NumericIndex > 0
BEGIN
SET #Numbers = RIGHT(#Numbers,LEN(#Numbers)-#NumericIndex+1)
SET #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
IF(#NonNumericIndex = 0)
BEGIN
INSERT
INTO #Results VALUES (#Numbers)
RETURN
END
ELSE
INSERT
INTO #Results VALUES
(LEFT(#Numbers,#NonNumericIndex-1))
SET #Numbers = RIGHT(#Numbers,LEN(#Numbers)-#NonNumericIndex+1)
SET #NumericIndex = PATINDEX('%[0-9]%',#Numbers)
END
RETURN
END