Calculate product of column values on the basis of other column in SQL Server - sql

I have a table
Tid Did value
------------------
1 123 100
1 234 200
2 123 323
2 234 233
All tids have dids as 123 and 234. So for every tid having dids 123 and 234 I want the product of corresponding values
The output table will be
Tid Product
------------------
1 20000 (product of 100 and 200)
2 75259 (product of 323 and 233)
Any help?

select tid,
min(case when did = 123 then value end)
* min(case when did = 234 then value end) product
from my_table
group by tid
To get the data for multiple rows combined (based on tid) you use GROUP BY.
Because you're grouping by tid, you have to use an aggregate function to do anything with other values from the individual rows. If implied assumptions hold (exactly 1 row matching each did for each tid) then it doesn't matter much what aggregate function you use; min is as good as anything.
Within the aggregation, you use CASE logic to select value for the required did (and NULL for all other rows in the tid group).
Then just do the math.

You can use some arithmetic to get the product per tid.
select tid,exp(sum(log(value))) as prod
from t
group by tid
To do this only for tid's having did values 123 and 234, use
select tid,exp(sum(log(value))) as prod
from t
group by tid
having count(distinct case when did in (123,234) then did end) = 2

Here's a Rexster solution, based on good work of #gbn here
SELECT
Tid,
CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END
FROM
(
SELECT
Tid,
SUM(LOG(ABS(NULLIF(value, 0)))) AS ABSMult,
SUM(SIGN(CASE WHEN value < 0 THEN 1 ELSE 0 END)) AS Neg,
MIN(ABS(value)) AS MinVal
FROM
t
GROUP BY
Tid
) t2

Related

Complex SQL Query - i found no answer :(

i've have a MS-SQL table which contains the following columns:
ID, SKU, StockID, StockQTY
The columns/rows are filled like:
ID
SKU
StockID
StockQTY
1
1111
1
12
2
1111
13
20
3
2222
1
0
4
2222
13
5
5
3333
1
0
6
3333
13
4
Now i need a SQL query which show all SKU (second column) which have a StockQTY = 0 in StockID = 1 AND a StockQTY > 1 in StockID 13. All other rows should not be shown (in this example row 1 and 2 should not be shown in the result).
Have anyone an idea how to realize this?
You can use a correlated subquery to test for skus that have 0 qty in StockID 1 and then just keep anything that has a qty greater than 1 in your StockID 13.
SELECT SKU
FROM yourtable yt
WHERE Stockid = 13
AND NOT EXISTS (SELECT 1 FROM yourtable WHERE StockID = 1 AND yt.SKU = SKU)
HAVING Sum(StockQty) > 1
Try something like:
SELECT SKU FROM yourTableName WHERE
(StockQTY = 0 AND StockID = 1) OR (StockQTY > 1 AND StockID = 13);
To pull data obviously it is a SELECT statement, you could use * instead of SKU if you want ALL the data instead of just the SKU number. Exchange yourTableName with the actual table name. The WHERE clause is the meat of your question. This use of OR and AND can be confusing. We have 2 cases we are testing. One where the stock is 0 but the ID is 1, but we use the OR so that regardless if that is found or not, we can then do the next part where the StockQTY is 1 and the StockID is 13. You need the and for both conditions in the own case, and you want OR so that either of these two cases will pull up.

Duplicate id rows with few columns to unique id row with many columns Oracle SQL

I have a pole table that can have one to four streetlights on it. Each row has a pole ID and the type (a description) of streetlight. I need the ID's to be unique with a column for each of the possible streetlights. The type/description can anyone of 26 strings.
I have something like this:
ID Description
----------------
1 S 400
1 M 200
1 HPS 1000
1 S 400
2 M 400
2 S 250
3 S 300
What I need:
ID Description_1 Description_2 Description_3 Description_4
------------------------------------------------------------------
1 S 400 M 200 HPS 1000 S 400
2 M 400 S 250
3 S 300
The order the descriptions get populated in the description columns is not important, e.g. for ID = 1 the HPS 1000 value could be in description column 1, 2, 3, or 4. So, long as all values are present.
I tried to pivot it but I don't think that is the right tool.
select * from table t
pivot (
max(Description) for ID in (1, 2, 3))
Because there are ~3000 IDs I would end up with a table that is ~3001 rows wide...
I also looked at this Oracle SQL Cross Tab Query But it is not quite the same situation.
What is the right way to solve this problem?
You can use row_number() and conditional aggregation:
select
id,
max(case when rn = 1 then description end) description_1,
max(case when rn = 2 then description end) description_2,
max(case when rn = 3 then description end) description_3,
max(case when rn = 4 then description end) description_4
from (
select t.*, row_number() over(partition by id order by description) rn
from mytable t
) t
group by id
This handles up to 4 descriptions per id. To handle more, you can just expand the select clause with more conditional max()s.

select query - eliminate rows with duplicate column value on condition

I have a select query that ends up with results like:
ID COMPLIANT
------------------
10 0
12 0
29 0
29 1
43 1
44 1
44 0
How can I get results without these duplicate ID rows, on the condition that if an ID has already been marked as COMPLIANT once (a 1 instead of a 0), the duplicate rows with COMPLIANT=0 do not appear? I'd want:
ID COMPLIANT
------------------
10 0
12 0
29 1
43 1
44 1
How about aggregation?
select id, max(complaint) as complaint
from t
group by id;
This returns one row per id. If you can have multiple complaints -- and you want all of those -- than an alternative is:
select id, complaint
from t
where complaint = 1
union all
select id, complaint
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.complaint = 1);
this will work:
select id, max(complaint)
from tablename
group by id;

Sql to make generic query in order to get resultant table

I have following table named tblItemLocations.
locationName pickRouteOrder ispickable itemnumber
Loc1 124 1 10-001
Loc2 126 0 10-001
Loc3 128 1 10-002
Loc4 130 0 10-004
Loc44 136 0 10-004
Loc5 131 1 10-007
Loc6 133 1 10-008
Needed result :
Foreach itemnumber , there should be only one record with ispickable=1.
If Ispickable=0, then show No Loc.
Also total number of record is equal to total number of distinct itemnumber.
e.g in above table , there are 5 distinct itemnumber (10-001,10-002,10-004,10-007,10-008).
So there should be 5 records in result table.
Resultant table :
locationName pickRouteOrder itemnumber
Loc1 124 10-001
Loc3 128 10-002
No Loc 130 10-004
Loc5 131 10-007
Loc6 133 10-008
Can you please help to make query in order to get resultant table?
Try this (tested using sql-sever and mysql):
select
coalesce( max(case when ispickable = 1 then locationName
else null end ), 'no loc') as locationName,
min( pickRouteOrder ) pickRouteOrder,
itemnumber
from test
group by itemnumber
The above does not generate the expected output when there are multiple records of an item and the one with the minimum pickRouteOrder is non-pickable (ispickable = 0).
Try below instead (modified from Gordon Linoff answer):
select locationName, pickRouteOrder, itemnumber
from test
where ispickable = 1
union all
select 'no loc', min(pickRouteOrder), itemnumber -- use group by to eliminate multiple non-pickable records
from test t
where ispickable = 0 and
not exists (select 1 from test t2 where t2.itemnumber = t.itemnumber
and t2.ispickable = 1)
group by itemnumber;
If at most one row is pickable and unpickable (as with the sample data), you can also approach this as:
select locationName, pickRouteOrder, itemnumber
from t
where ispickable = 1
union all
select 'no loc', pickRouteOrder, itemnumber
from t
where ispickable = 0 and
not exists (select 1 from t t2 where t2.itemnumber = t.itemnumber and t2.ispackable = 1);
Under many circumstances, this will be faster than an aggregation solution, although I also like that version as well.

Update column value of one row from other rows

I have the following table:
sno name pid amount total
1 Arif 0 100 null
2 Raj 1 200 null
3 Ramesh 2 100 null
4 Pooja 2 100 null
5 Swati 3 200 null
6 King 4 100 null
I want total of each person such that it gives total sum of amount of its descendants.
For ex.
for RAJ total will be : total= amount of(raj+ramesh+pooja+swati+king)
for SWATI :Total=amount of swati only.
You could try something like this:
WITH hierarchified AS (
SELECT
sno,
amount,
hierarchyID = CAST(sno AS varchar(500))
FROM yourTable
WHERE pid = 0
UNION ALL
SELECT
t.sno,
t.amount,
hierarchyID = CAST(h.hierarchyID + '/' + RTRIM(t.sno) AS varchar(500))
FROM yourTable t
INNER JOIN hierarchified h ON t.pid = h.sno
)
UPDATE yourTable
SET total = t.amount + ISNULL(
(
SELECT SUM(amount)
FROM hierarchified
WHERE hierarchyID LIKE h.hierarchyID + '/%'
),
0
)
FROM yourTable t
INNER JOIN hierarchified h ON t.sno = h.sno;
Note that this query (which you can try on SQL Fiddle) would probably not be very efficient on a large dataset. It might do as a one-off query, and then it would likely be better to organise updating the totals each time the table is updated, i.e. using triggers.