multiply rows in t-sql - sql

I have following table
ID Number
----------------
1 41.5
2 42.5
3 43.5
2 44.5
2 45.5
1 46.5
1 47.5
I need to write a query which will return distinct ID's and corresponding Number column values multiplied. For the given table result should be like this
ID Result
-----------------
1 41.5 * 46.5 * 47.5
2 42.5 * 44.5 * 45.5
3 etc...
(without use cursors)

SELECT Id, EXP(SUM(LOG(Number))) as Result
FROM Scores
GROUP BY id
This will work for positive numbers, to multiply negative numbers as well you can use ABS() function to use absolute (positive) value but final result will be positive rather than negative number:
SELECT Id, EXP(SUM(LOG(ABS(Number)))) as Result
FROM Scores
GROUP BY id
EDIT: Added test script
DECLARE #data TABLE(id int, number float)
INSERT INTO #data VALUES
(1, 2.2),
(1, 10),
(2, -5.5),
(2, 10)
SELECT Id, EXP(SUM(LOG(ABS(Number)))) as Result
FROM #data GROUP BY id
Output:
1 22
2 55

select id, power(sum(log10(num)),10) group by id

This is a slight variation on row concatenation and Jeff Moden has an excellent article on that at SQL Server Central titled Performance Tuning: Concatenation Functions and Some Tuning Myths
Edit: #mellamokb It is analogous to concatenation, but requires some modification. A sample script would be
create table testMult (id int, num int)
GO
insert into testMult values (1, 2)
insert into testMult values (1, 3)
insert into testMult values (1, 4)
insert into testMult values (2, 2)
GO
create function dbo.fnMult (#someId int)
returns int as
begin
declare #return int
set #return = 1
select #return = #return * num
from testMult
where id = #someId
return #return
end
GO
select *
from testMult
select t1.id,
dbo.fnMult(t1.id)
from testMult t1
group by t1.id
Which is just a very small variation on the script provided by Jeff Moden in his article.

Related

SQL different between sum(c1)*sum(c2) and sum(c1*c2)

In SQL language what's the different results or performence between
sum(c1) * sum(c2) from t
and
sum(c1 * c2) from t
I use it to sum sales totals
select sum(price * quantity) as total from Bill
so what's the best choice?
Vertical and Horizontal summations ... assuming no NULLS
declare #mytable table (a int, b int)
insert into #mytable
values
(1,1),
(2,3)
select * from #mytable
select sum(a) * sum(b) from #mytable -- result is 12 .. vertical/column, summarize column first then multiply to other column
select sum(a*b) from #mytable --- result is 7 .. horizontal/row, summarize the product of a and b
This is not a matter of SQL. It's just basic math. Assuming c1=(0,1) and c2=(2,3). Then:
sum(c1)*sum(c2) alias... (0+1)*(2+3) = 5
is not the same as:
sum(c1*c2) alias... (0*2)+(1*3) = 3
NULLs and Zeros are one concern
Declare #YourTable table (C1 money,C2 money)
Insert Into #YourTable values
(100,10)
,(100,null)
Select sum(c1) * sum(c2) -- 2000.00
From #YourTable
Select sum(c1 * c2) -- 1000.00
From #YourTable

Adding total row and Increasing quality for an sql query based on multiple parameters

I have a table in Microsoft Sql Server 2008 like:
id name timelong
1 Eray 2
1 Jack 1
1 Ali 7
1 john 3
1 Roby 5
1 Mike 4
1 Josh 11
What I want to do is to select data based on user multi-selectable parameters. Think that there are 4 checkboxes: 0-3,4-6,7-9,10-12 and users can select more than one checkbox. Only those data user selected should be seen and a TOTAL row needs to be added at the bottom.
What I tried is on the bottom but it is not working well - TOTAL row is not there. My question is how I can add the Total row there, and is there any more professional way to provide this query. Thanks.
declare #interval03 bit -- 0 to 3
declare #interval06 bit -- 4 to 6
declare #interval09 bit -- 7 to 9
declare #interval12 bit -- 10 to 12
Select *, sum(timelong)
From myTable
Where (#interval03=1 and timelong<4)
or
(#interval06=1 and timelong>3 and timelong<7)
or
(#interval09=1 and timelong>6 and timelong<10)
or
(#interval12=1 and timelong>9 and timelong<13)
group by id, name
Try grouping sets:
Select ID, isnull(Name, 'TOTAL'), sum(timelong)
From myTable
Where (#interval03=1 and timelong <= 3)
or
(#interval06=1 and timelong between 4 and 6)
or
(#interval09=1 and timelong between 7 and 9)
or
(#interval12=1 and timelong >= 10)
group by grouping sets ((ID, name), ())
Assuming from your query that timelong is an int, I've simplified your where a little as well.
More information on grouping sets, rollup, and cube: https://technet.microsoft.com/en-us/library/bb522495(v=sql.105).aspx
TRY This..One more way to add totals...
declare #table Table (id INT,name VARCHAR(10), timelong INT)
insert into #table (id ,name, timelong) VALUES (1, 'Eray', 2)
insert into #table (id ,name, timelong) VALUES (1 ,'Jack' ,1)
insert into #table (id ,name, timelong) VALUES (1 ,'Ali' , 7)
insert into #table (id ,name, timelong) VALUES (1 ,'john' ,3)
insert into #table (id ,name, timelong) VALUES (1 ,'Roby' , 5)
insert into #table (id ,name, timelong) VALUES (1 ,'Mike' ,4)
insert into #table (id ,name, timelong) VALUES (1 ,'Josh' ,11)
declare #interval03 bit=1 -- 0 to 3
declare #interval06 bit -- 4 to 6
declare #interval09 bit -- 7 to 9
declare #interval12 bit -- 10 to 12
DECLARE #result TABLE (ID INT,Name VARCHAR (30),TimeLong INT)
INSERT INTO #result
Select id, name, sum(timelong) timelong
From #table
Where (#interval03=1 and timelong<4)
or (#interval06=1 and timelong>3 and timelong<7)
or (#interval09=1 and timelong>6 and timelong<10)
or (#interval12=1 and timelong>9 and timelong<13)
group by id, name
INSERT INTO #result
SELECT MAX(ID) +100 ID,'Total' Name,SUM(TimeLong) TimeLong
FROM #result
HAVING COUNT(*)<>0
SELECT * FROM #result

Random Function behavior in SELECT query in SQL Server

I have a written a random function dbo.UDF_Q_RandomNumber() that generates a floating type random number between 0 and 1.
DECLARE #upper = 10
DECLARE #lower = 1
SELECT
ROUND(CAST((#lower + (#upper - #lower) * dbo.UDF_Q_RandomNumber()) AS INT), 0)
The above code generates a random number between 1 and 10.
Now I created a temporary table #tempTable with 10 rows in it with columns Id and Number.
Id Number
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
SQL query:
CREATE TABLE #tempTable(Id INT, Number INT)
INSERT INTO #tempTable VALUES (1,1)
INSERT INTO #tempTable VALUES (2,2)
INSERT INTO #tempTable VALUES (3,3)
INSERT INTO #tempTable VALUES (4,4)
INSERT INTO #tempTable VALUES (5,5)
INSERT INTO #tempTable VALUES (6,6)
INSERT INTO #tempTable VALUES (7,7)
INSERT INTO #tempTable VALUES (8,8)
INSERT INTO #tempTable VALUES (9,9)
INSERT INTO #tempTable VALUES (10,10)
DECLARE #maxCount INT;
SELECT #maxCount= COUNT(1) FROM #tempTable
SELECT * FROM #tempTable
SELECT Number
FROM #tempTable
WHERE Id = ROUND(CAST((1+(#maxCount-1)*dbo.UDF_Q_RandomNumber())AS INT),0)
DROP TABLE #tempTable
Here the query
SELECT Number
FROM #tempTable
WHERE Id = ROUND(CAST((1+(#maxCount-1)*dbo.UDF_Q_RandomNumber()) AS INT), 0)
Sometimes it returns 2 rows and sometimes null which should not come as Id selected is between 1 and 10 (the rows in temptable) and every Id has value too.
Please help .
Without seeing the function, it's hard to say, but I suspect your function is non-deterministic, so it gets interpreted for each row of the table.
If you use rand() it only returns one row
SELECT Number FROM #tempTable WHERE Id= ROUND(CAST((1+(#maxCount-1)*rand())AS INT),0)
Alternatively, if you just want a random #n rows...
select top (#n) * from #tempTable order by newid()

SQL Query to return rows where a list of numbers is between start and end values

There is a table in Oracle with the columns:
id | start_number | end_number
---+--------------+------------
1 | 100 | 200
2 | 151 | 200
3 | 25 | 49
4 | 98 | 99
5 | 49 | 100
There is a list of numbers (50, 99, 150).
I want an sql statement that returns all the ids where any of the numbers in the list of numbers is found equal to or between the start_number and the end_number.
Using the above example; 1, 4 and 5 should be returned.
1 - 150 is between or equal to 100 and 200
2 - none of the numbers are between or equal to 151 and 200
3 - none of the numbers are between or equal to 25 and 49
4 - 99 is between or equal to 98 and 99
5 - 50 and 99 are between or equal to 49 and 100
drop table TEMP_TABLE;
create table TEMP_TABLE(
THE_ID number,
THE_START number,
THE_END number
);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (1, 100, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (2, 151, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (3, 25, 49);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (4, 98, 99);
insert into TEMP_TABLE(the_id, the_start, the_end) values (5, 49, 100);
The following is the solution I came up with based on the comments and answers below plus some additional research:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(SYS.DBMS_DEBUG_VC2COLL(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
This works too:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(sys.ku$_vcnt(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
Here is a full example:
create table #list (
number int
)
create table #table (
id int,
start_number int,
end_number int
)
insert into #list values(50)
insert into #list values(99)
insert into #list values(150)
insert into #table values(1,100,200)
insert into #table values(2,151,200)
insert into #table values(3,25,49)
insert into #table values(4,98,99)
insert into #table values(5,49,100)
select distinct a.* from #table a
inner join #list l --your list of numbers
on l.number between a.start_number and a.end_number
drop table #list
drop table #table
You'll simply need to remove the code about #table (create, insert and drop) and put your table in the select.
It partly depends on how your are storing your list of numbers. I'll assume that they're in another table for now, as even then you have many options.
SELECT
*
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourList WHERE number BETWEEN yourTable.start_number AND yourTable.end_number)
Or...
SELECT
*
FROM
yourTable
INNER JOIN
yourList
ON yourList.number BETWEEN yourTable.start_number AND yourTable.end_number
Both of those are the simplest expressions, and work well for small data sets. If your list of numbers is relatively small, and your original data is relatively large, however, this may not scale well. This is because both of the above scan the whole of yourTable and then check each record against yourList.
What may be preferable is to scan the list, and then attempt to use indexes to check against the original data. This would require you to be able to reverse the BETWEEN statement to yourTable.start_number BETWEEN x and y
This can only be done if you know the maximum gap between start_number and end_number.
SELECT
*
FROM
yourList
INNER JOIN
yourTable
ON yourTable.end_number >= yourList.number
AND yourTable.start_number <= yourList.number
AND yourTable.start_number >= yourList.number - max_gap
To achieve this I would store the value of max_gap in another table, and update it as the values in yourTable change.
You will want to create a temporary table to hold your numbers, if the numbers aren't already in one. Then it becomes relatively simple:
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN TempTable tt --your list of numbers
ON tt.number Between mt.start_number and mt.end_number
To create the table based on an array of passed values, you can use table definitions in your procedure. I'm light on Oracle syntax and don't have TOAD handy, but you should be able to get something like this to work:
CREATE OR REPLACE PROCEDURE FindIdsFromList
AS
DECLARE
TYPE NumberRecord IS RECORD (Number int NOT NULL)
TYPE NumberList IS TABLE OF NumberRecord;
NumberList myNumberList;
BEGIN
myNumberList := (50,99,150);
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN myNumberList nt --your list of numbers
ON nt.Number Between mt.start_number and mt.end_number
END

MySQL: Is it possible to return a "mixed" dataset?

I'm wondering if there's some clever way in MySQL to return a "mixed/balanced" dataset according to a specific criterion?
To illustrate, let's say that there are potential results in a table that can be of Type 1 or Type 2 (i.e. a column has a value 1 or 2 for each record). Is there a clever query that would be able to directly return results alternating between 1 and 2 in sequence:
1st record is of type 1,
2nd record is of type 2,
3rd record is of type 1,
4th record is of type 2,
etc...
Apologies if the question is silly, just looking for some options. Of course, I could return any data and do this in PHP, but it does add some code.
Thanks.
Something like this query should do:
Select some_value, x, c
From
(
Select
some_value, x,
Case When x=1 Then #c1 Else #c2 End As c,
#c1 := Case When x=1 Then #c1+2 Else #c1 End As c1,
#c2 := Case When x=2 Then #c2+2 Else #c2 End As c2
From test_data, (Select #c1:=0, #c2:=1) v
Order By some_value
) sub
Order By c
It assigns unique even numbers to x=0, and odd numbers to x=1, and uses these values as sort criteria.
It returns
some_value x c
A 1 0
X 2 1
B 1 2
Y 2 3
C 1 4
Z 2 5
for the following test-data:
Create Table test_data (
some_value VARCHAR(10),
x INT
);
Insert Into test_data Values('A', 1);
Insert Into test_data Values('B', 1);
Insert Into test_data Values('C', 1);
Insert Into test_data Values('Z', 2);
Insert Into test_data Values('Y', 2);
Insert Into test_data Values('X', 2);
Within the alternating rule values are sorted by some_value, you can change this in the inner select, or add your conditions there.
If there are more values of a certain type (1 or 2), you get them after the rest (1 2 1 2 2 2).
You can use IF function as a part of your SELECT statement to change columns, but I'm not sure how to make is alternate automatically between two columns. If you however find proper condition this will work for you
SELECT IF(condition, first_column, second_column) FROM your_table
first_column and second_column can be of different type, for example:
SELECT IF(id > 10, name, status_id) FROM clients
works well when name is a VARCHAR and status_id is an INT