subtract operation on two rows of temp table in SQL server - sql

Age16_20 AgeBelow_16 Age21_30 Age31_40 Age41_50 Age51_60 QID
93 81 55 46 54 48 1
13 11 15 16 14 18 2
I want to subtract second row from first? which is best way to do inside stored procedure. performance main issue here so please provide optimal solution.

SELECT
[qid1].AgeBelow16 - [qid2].AgeBelow16 AS [AgeBelow16],
[qid1].Age16_20 - [qid2].Age16_20 AS [Age16_20],
[qid1].Age21_30 - [qid2].Age21_30 AS [Age21_30],
[qid1].Age31_40 - [qid2].Age31_40 AS [Age31_40],
[qid1].Age41_50 - [qid2].Age41_50 AS [Age41_50],
[qid1].Age51_60 - [qid2].Age51_60 AS [Age51_60]
FROM
MyTable AS [qid1]
INNER JOIN
MyTable AS [qid2]
ON [qid1].QID = [qid2].QID - 1
WHERE
[qid1].QID = 1
If possible, however, you would be much better off storing the QID2 values as negatives. That way you don't need to know which one to subtract from the other; it's just a straight SUM.
SELECT
SUM(AgeBelow16) AS [AgeBelow16], -- (93) + (-13) = 80
SUM(Age16_20) AS [Age16_20], -- (81) + (-11) = 70
SUM(Age21_30) AS [Age21_30], -- (55) + (-15) = 40
SUM(Age31_40) AS [Age31_40], -- (46) + (-16) = 30
SUM(Age41_50) AS [Age41_50], -- (54) + (-14) = 40
SUM(Age51_60) AS [Age51_60] -- (48) + (-18) = 30
FROM
MyTable

Assuming you have a unique identifier on the rows (we'll presume it is ID) you could do something like (I have only done the first column, for brevity):
SELECT SUM(Age16_20) FROM
(SELECT Age16_20
FROM table
WHERE ID = 1
UNION ALL
SELECT -Age16_20
FROM table
WHERE ID = 2) Temp

Assumes only 2 rows.
SELECT
SUM(CASE WHEN ID = 2 THEN -Age16_20 ELSE Age16_20 END),
SUM(CASE WHEN ID = 2 THEN -AgeBelow_16 ELSE AgeBelow_16 END),
SUM(CASE WHEN ID = 2 THEN -Age21_30 ELSE Age21_30 END),
...etc
FROM
Mytable
If more then 2 rows (why?) then change the CASE to
CASE ID WHEN 1 THEN Age16_20 WHEN 2 THEN -Age16_20 ELSE NULL END

Related

SQL query help: How to evaluate value/max(value) based on some conditions in same query

Not an expert in SQL. I am using postgres database with EF migration. Stuck with this requirement. Here it goes.
My table is like this:
A B C D
20 1 1 1
59 0 0 1
57 1 1 1
10 1 0 0
30 1 1 1
15 0 0 0
The order of rows is like oldest to latest(top to bottom).
Half query I have from my project is as below:
SELECT dcr."A"
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."B" != 0
ORDER BY "UtcDate" desc
limit(1)
This will fetch me the first part value of latest A when it matches condition. But not the Max part and the division part as explained below.
I want to find the result of ((latest A where B = C = D = 1 divided by max of A in its previous rows where B = C = D = 1) - 1) * 100.
I want this to happen in single query and there are multiple groups like this. Lets say the table contains around 60 rows and we can group them based on some other column. Each group should evaluate this above formula.
Expected result for above example should be:
result = ((30 / 57) - 1) * 100 = (0.5263 - 1) * 100 = -47.73
You can use a subquery to get the max. I don't know why you're writing the query in that strange style, but I will keep it:
SELECT dcr."A" / (SELECT MAX("A")
FROM "DCR"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0)) - 1) * 100
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0
ORDER BY "UtcDate" desc
limit(1)
maybe something like this?
select (t1."A"/max(t2."A"))*100 from
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t1
join
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t2
on t1.id>t2.id
group by t1."A",t1."E"

How to provide dynamic usage of select query in SQL Server 2008 based on a variable value

I have a table myTable with these columns and sample data:
Id Name Period0Status Period1Status Period2Status
-------------------------------------------------------
1 Mark 1 2 3
2 John 2 3 3
3 Brad 1 1 1
4 John 3 3 3
5 Mark 1 3 2
etc...
What I want is to use those data but the column names should be taken according to a SQL variable. What I need is something like that:
declare #0_period varchar(50) = 'Period_0'
declare #1_period varchar(50) = 'Period_1'
declare #2_period varchar(50) = 'Period_2'
declare #counter = 0;
while #counter < 3
begin
insert into #Results(Period, JohnVolume, MarkVolume, JoshVolume)
select
'#' + #counter + '_period',
sum(case when(name = 'John' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Mark' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Brad' and (Period+#counter+Status = 3) then 1 else 0))
from
myTable
set #counter = #counter + 1
end
I could not find the correct syntax to provide the dynamic query here. Any help would be appreciated. SQL Server 2008 is used.
Output table I want to produce be like:
Period JohnVolume MarkVolume BradVolume
Period_0 1 0 0
Period_1 2 1 0
Period_2 2 1 0
It should basically counts the amount of 3s for each (John,Mark and Brad) for each period. I gotta find out how to correct the syntax for the parts that #counter is used inside the select query.
Here's an example of how you could do this, with a cross apply to derive the values for each period and a count(case...) for the end values:
SELECT period
, COUNT(CASE WHEN Name = 'John' AND val = 3 THEN 1 END) [JohnVolume]
, COUNT(CASE WHEN Name = 'Mark' AND val = 3 THEN 1 END) [MarkVolume]
, COUNT(CASE WHEN Name = 'Brad' AND val = 3 THEN 1 END) [BradVolume]
FROM myTable
CROSS APPLY (VALUES ('Period_0', Period0Status), ('Period_1', Period1Status), ('Period_2', Period2Status)) unp(period, val)
GROUP BY period
The cross apply works as an unpivot to get each period status and the values within them, and the conditional case statements work as a pivot.
Alternatively, you could use an actual UNPIVOT/PIVOT if you want. An example would be:
SELECT Period, John JohnVolume, Mark MarkVolume, Brad BradVolume
FROM (
SELECT *
FROM myTable
UNPIVOT (val FOR period IN (Period0Status, Period1Status, Period2Status)) U
WHERE val = 3) T
PIVOT (COUNT(val) FOR Name IN ([John], [Mark], [Brad])) P

Conditional SUM with SELECT statement

I like to sum values in a table based on a condition taken from the same table called. The structure of the table as per below. The table is called Data
Data
Type Value
1 5
1 10
1 15
1 25
1 15
1 20
1 5
2 10
3 5
If the Value of Type 2 is larger than the Value of Type 3 then I like to subtract the Value of Type 2 from the sum of all the Values in the table. I'm not sure how to write the IF statements using Values looked up in the table. I have tried below but it doesn't work.
SELECT SUM(Value)-IF(SELECT Value FROM Data WHERE Type=2>SELECT Value
FROM Data WHERE Type=3 THEN SELECT Value FROM Data
WHERE Type=2 ELSE SELECT Value FROM Data WHERE Type=3) FROM Data
or
SELECT SUM(d.Value)-IIF(a.type2>b.type3, a.type2, b.type3)
FROM Data d, (SELECT Value AS type2 FROM Data WHERE Type=2) a,
(SELECT Value AS type3 FROM Data WHERE Type=3) b
If I follow your logic correctly, then this would seem to do what you want:
select d.value - (case when d2.value > d3.value then d2.value else 0 end)
from data d cross join
(select value from data where type = 2) d2 cross join
(select value from data where type = 3) d3 ;
EDIT:
If you want just one number, then use conditional aggregation:
select sum(value) -
(case when sum(case when type = 2 then value else 0 end) >
sum(case when type = 3 then value else 0 end)
then sum(case when type = 2 then value else 0 end)
else 0
end)
from data;
Thanks for pointing me in the right direction. This is what I came up with in the end. It is a little bit different to the reply above since I'm using MS Access
SELECT SUM(Value)-IIf(SUM(IIf(Type=2, Value, 0)>SUM(IIf(Type=3, Value, 0), SUM(IIf(Type=2, Value, 0), SUM(IIf(Type=3, Value, 0) FROM Data
It is them same as the second suggestion above but adapted to MS Access SQL.

SQL - Find the binary representation from the place of '1's

It is really hard to find a good title for this.
Here is the question: I have a SELECT query GROUP BY a field which returns me up to three values (1,2,3). These values are representing the positions of '1' in a binary number.
In other words:
Query Output | Reult
0,1,2 | 7 (111)
1,2 | 6 (110)
3 | 1 (001)
- | 0 (000)
Ok, I know it is easy. But there are two constraints. First, I want a query not a function/store procedure. Second, the result should be a string (like '010') not the number.
I found the solution for integer value, but not the string (varchar)
SELECT COALESCE(sum(power(2, field)), 0) AS test FROM (
SELECT field FROM myTable GROUP BY field) a
I am using SQL server 2008, just in case.
I also have this solution, but this one cannot be extended to bigger number of outputs:
SELECT output =
CASE TEST
WHEN 0 THEN '000'
WHEN 1 THEN '001'
WHEN 2 THEN '010'
WHEN 3 THEN '011'
WHEN 4 THEN '100'
WHEN 5 THEN '101'
WHEN 6 THEN '110'
WHEN 7 THEN '111'
END
FROM(
select COALESCE(sum(power(2, 3 - field)), 0) as test from (
select field from myTable group by field) a) b
You can use binary and and string concatenation:
select (case when test&4 > 0 then '1' else '0' end) +
(case when test&2 > 0 then '1' else '0' end) +
(case when test&1 > 0 then '1' else '0' end)
from (select 6 as test) t;
If you are allergic to case statements, you could do this:
select CHAR(ascii(0) + (test&4)/4) +
CHAR(ascii(0) + (test&2)/2) +
CHAR(ascii(0) + (test&1)/1)
from (select 6 as test) t

having issues sorting alpha numeric chars

I am trying to sort one column which have alphanumeric letters
see my query below
SELECT d.number
FROM table name d, table_name 2 a WHERE d.case_id ='11-41'
AND d.ExhibitTypeId = TypeId AND d.ComplianceNo = '0' and
active = 1 and number is not null order by case
when ISNUMERIC(d.number) = 1 then right('0000000000'+d.number+'0',10)
else right('0000000000'+d.number,10)
end
This is the output
1
2
3
11
12
2A1
I want this output instead
1
2
2A1
3
11
12
Any help regarding this is greatly appreciated.
If(ISNUMERIC(LEFT(case,2)
BEGIN
order by case
END
else
BEGIN
order by LEFT(case,1), LEFT(case,2)
END
Assuming SQL Server this may work with some tweaks
SELECT
d.number
FROM
table name d,
table_name 2 a
WHERE
d.case_id ='11-41'
AND
d.ExhibitTypeId = TypeId
AND
d.ComplianceNo = '0'
and
active = 1
and number is not null
order by
Convert(int, LEFT(number, Case
When PATINDEX('%[^0-9]%', number) > 0 Then PATINDEX('%[^0-9]%', number) - 1
Else LEN(number)
End)
),
LEN(Number)