How to do some aggregate calculation through several columns one by one? - sql

I am new to SQL and needed some help. I have a table that has some numeric values and I need to populate a column (all values null) with calculations from other columns in the table.
For example. I have some values and a total value. I need another column to calculate the percentage between those two.
I have many columns that need populating from calculation based on different columns. For eg. A column name "risk1" will help me calculate and populate another column called "1per". My code looks something like this:
UPDATE DPRA2_Export
SET "1Per" = ((CAST(Risk1 AS DECIMAL (38,2))/CAST(GrandTotal AS DECIMAL(38,2))) * 100);
UPDATE DPRA2_Export
SET "2Per" = ((CAST(Risk2 AS DECIMAL (38,2))/CAST(GrandTotal AS DECIMAL(38,2))) * 100);
UPDATE DPRA2_Export
SET "3Per" = ((CAST(Risk3 AS DECIMAL (38,2))/CAST(GrandTotal AS DECIMAL(38,2))) * 100);
.................
It goes on like this.
Is there a way I can for-loop this thing instead of writing over and over again. The only thing that changes in the code is the column name "Risk%" and the SET column name "%Per"
Any ideas?

First, having columns with sequential names -- , , . . . -- is suspicious. In general, this format is not a good fit for relational databases. Instead, the data should be stored with one row per risk and an identifier for the risk.
Such tables are often useful for output purposes, but not for storing data.
Second, the data types for the division are probably entirely unnecessary. If you are using a database that does integer division and your values are integers, you can convert using a simpler method such as '*1.0'.
Finally, you can issue a single update:
UPDATE DPRA2_Export
SET "1Per" = Risk1*1.0 / GrandTotal,
"2Per" = Risk2*1.0 / GrandTotal,
"3Per" = Risk3*1.0 / GrandTotal ;
You can construct the logic by querying for the columns, using the information_schema tables.

Related

SQL to powerBI expression?

How to write this expression in PowerBI
select distinct([date]),Temperature from Device47A8F where Temperature>25
Totally new to PowerBI. Is there any tool that can change the query from sql to PowerBI expression?
I have tried so many type of different type of expressions but getting error, Most of the time I am getting this:
The expression refers to multiple columns. Multiple columns cannot be converted to a scalar value.
Need help, Thanks.
After I posted my answer, wondered if your expected result is get only one date by temperature, In other words, without repeated dates in your result set.
A side note: select distinct([date]),Temperature from Device47A8F where Temperature>25 returns repeated dates since DISTINCT keyword evaluate distinct columns values specified in the SELECT statement, it doesn't return distinct values in a specific column even if you surround it with parenthesis.
Now what brings us here. What I can see in your error is that you are trying to use a table-valued (produces a table with multiple columns) expression in a measure which only accepts scalar-valued (calculate only one value).
Supposing you have a table like this:
Running your SQL query you will get the highlighted in yellow rows:
You can see 01/09/2016 date is repeated. If you want to create a measure you have to define what calculation you want to show for temperature. i.e, average, max or min etc.
In the below expression is being calculated the maximum temperature greater than 25 per date:
MaxTempGreaterThan25 =
CALCULATE ( MAX ( Device47A8F[Temperature] ), Device47A8F[Temperature] > 25 )
In this case the measure MaxTempGreaterThan25 is calculated per date.
If you don't want to produce a measure but a table. In the Power BI Tool bar select Modeling tab and click New Table icon.
Use this expression:
MyTemperatureTable =
FILTER ( Device47A8F, Device47A8F[Temperature] > 25 )
It should produce a new table named MyTemperatureTable like this:
I recommend you learn some basics about DAX, it is pretty different from SQL / T-SQL and there are things you can't do depending on your model and data.
Let me know if this helps.
You probably don't need to write any code if your objective is to show the result in a Power BI visual e.g. a table. Power BI naturally aggregates data if the datatype is numeric (e.g. Temperature).
I would just add a Table visual on a Report page and add the Date and Temperature columns to it. Then in Visualizations / Fields / Values I would click the little down-arrow on the Temperature field and set the Aggregation e.g. Maximum. Then in Visualizations / Fields / Filters I would click the little down-arrow on the Temperature field and set the Filter e.g. is greater than: 25
Hard-coded solutions are unlikely to survive the next question from your users e.g. "but what if I want to see Temperature > 24? Or 20? Or 30?"

String ending in range of numbers

I have a column with data of the following structure:
aaa5644988
aaa4898494
aaa5642185
aaa5482312
aaa4648848
I have a range that can be anything, like 100-30000 or example. I want to have all values that end in numbers between that range.
I tried
like '%[100-30000]'
but this doesn't work apparently.
I have seen a lot of similar questions but none of the solved my problem
edit I'm using SQL server 2008
Example:
Value
aaa45645695
aaa28568720
aaa65818450
8789212
6566700
For the range 600-1200, I want to retrieve row 1,2,5 because they end with the range.
In SQL, like normally only support % and _ these two operators. That's why like '%[100-30000]' doesn't work.
Depend on your use case, there could be two solutions for this problem:
If you only need to do this query two or three times(didn't care how long it takes), or the dataset is not very big. You can select all the data from this column, and then do the filtering in another programming language.
Take ruby for example, you can do:
column_data = #connection.execute("select * from your_column_name")
result = column_data.map{|x| x.gsub(/^.*[^\d]/, '').to_i }.select{|x| x > 100 && x < 30000}
If you need to do this query regularly, I'd suggest you add a new column to this data table with only the numbers in the current column, so as to get a much better performance in querying speed.
SELECT *
FROM your_table
WHERE number_column BETWEEN 100 AND 30000

SQL - View column that calculates the percentage from other columns

I have a query from Access where I caluclated the percentage score of three seperate numbers Ex:
AFPercentageMajor: [AFNumberOfMajors]/([AFTotalMajor]-[AFMajorNA])
which could have values of 20/(23-2) = 95%
I have imported this table into my SQL database and tried to write a expression in the view (changed the names of the columns a bit)
AF_Major / (AF_Major_Totals - AF_Major_NA)
I tried adding *100 to the end of the statement but it only works if the calculation is at 100%. If it is anything less than that it puts it as a 0.
I have a feeling it just doesn't like the combincation of the three seperate column names. But like I said I'm still learning so I could be going at this completely wrong!
SQL Server does integer division. You need to change one of the values to a floating point representation. The following will work:
cast([AFNumberOfMajors] as float)/([AFTotalMajor]-[AFMajorNA])
You can multiply this by 100 to get the percentage value.

Splitting text in SQL Server stored procedure

I'm working with a database, where one of the fields I extract is something like:
1-117 3-134 3-133
Each of these number sets represents a different set of data in another table. Taking 1-117 as an example, 1 = equipment ID, and 117 = equipment settings.
I have another table from which I need to extract data based on the previous field. It has two columns that split equipment ID and settings. Essentially, I need a way to go from the queried column 1-117 and run a query to extract data from another table where 1 and 117 are two separate corresponding columns.
So, is there anyway to split this number to run this query?
Also, how would I split those three numbers (1-117 3-134 3-133) into three different query sets?
The tricky part here is that this column can have any number of sets here (such as 1-117 3-133 or 1-117 3-134 3-133 2-131).
I'm creating these queries in a stored procedure as part of a larger document to display the extracted data.
Thanks for any help.
Since you didn't provide the DB vendor, here's two posts that answer this question for SQL Server and Oracle respectively...
T-SQL: Opposite to string concatenation - how to split string into multiple records
Splitting comma separated string in a PL/SQL stored proc
And if you're using some other DBMS, go search for "splitting text ". I can almost guarantee you're not the first one to ask, and there's answers for every DBMS flavor out there.
As you said the format is constant though, you could also do something simpler using a SUBSTRING function.
EDIT in response to OP comment...
Since you're using SQL Server, and you said that these values are always in a consistent format, you can do something as simple as using SUBSTRING to get each part of the value and assign them to T-SQL variables, where you can then use them to do whatever you want, like using them in the predicate of a query.
Assuming that what you said is true about the format always being #-### (exactly 1 digit, a dash, and 3 digits) this is fairly easy.
WITH EquipmentSettings AS (
SELECT
S.*,
Convert(int, Substring(S.AwfulMultivalue, V.Value * 6 - 5, 1) EquipmentID,
Convert(int, Substring(S.AwfulMultivalue, V.Value * 6 - 3, 3) Settings
FROM
SourceTable S
INNER JOIN master.dbo.spt_values V
ON V.Value BETWEEN 1 AND Len(S.AwfulMultivalue) / 6
WHERE
V.type = 'P'
)
SELECT
E.Whatever,
D.Whatever
FROM
EquipmentSettings E
INNER JOIN DestinationTable D
ON E.EquipmentID = D.EquipmentID
AND E.Settings = D.Settings
In SQL Server 2005+ this query will support 1365 values in the string.
If the length of the digits can vary, then it's a little harder. Let me know.
Incase if the sets does not increase by more than 4 then you can use Parsename to retrieve the result
Declare #Num varchar(20)
Set #Num='1-117 3-134 3-133'
select parsename(replace (#Num,' ','.'),3)
Result :- 1-117
Now again use parsename on the same resultset
Select parsename(replace(parsename(replace (#Num,' ','.'),3),'-','.'),1)
Result :- 117
If the there are more than 4 values then use split functions

SQL Server LIKE for Integer

I have a column which is integer and I want to search all numbers starting with 1500.
I know I could use something like left(accountid, 4)= 1500.
But is that an optimum solution or is there a better approach?
I am using SQL Server 2005.
LEFT(CONVERT(varchar, accountid),4)="1500"
An INT is an INT is an INT - it's just a numeric value. An INT doesn't "look" like some string value..... if you want to compare with LIKE, you need a string - you need to convert your INT to a string to be able to do that.
If you need to search and compare against that string representation of your INT a lot, I would recommend making it a computed, persisted column on your table:
ALTER TABLE dbo.YourTable
ADD IntString AS LEFT(CAST(YourInt AS VARCHAR(20)), 4) PERSISTED
That way, you get a new column that has a value inside it, that value is always up to date, it's a persisted column, you can index it if needed - you get all the benefits of comparing your "int" with the LIKE operator :-)
If you're willing to trade storage space for performance speed, I would add a persisted, computed column to the table containing the string version of the integer, index appropriately, and use that column for searching.
Assuming you know the minimum and maximum values for your column, you can do this:
select
stuff
from
table
where
(number = 1500)
or (number >= 15000 and number <= 15009)
or (number >= 150000 and number <= 150099)
or (number >= 1500000 and number <= 1500999)
or (number >= 15000000 and number <= 15009999)
-- add however many statements you need.
Or if you don't know the minimums and maximums, you could do this:
...
where
(number - 1500*POWER(10, FLOOR(LOG10(number) - 3)))
< (POWER(10, FLOOR(LOG10(number) - 3)))
If you want to go even further, why not create like a super category for accounts and that would eliminate the need to perform LEFT() on a converted varchar from an integer. To clarify, if you know all accounts beginning with id 1500 are say, accounts related to sales, you could have a super category of 1500 for all of them. This certainly only applies if this hierarchical relationship exists =).
It all depends on the volume of data. If you are talking about millions of records then using left with convert will not be quick. The best possible option would be have a computed column which stores the first four digits and you doing a search from it directly would be the fastest. But every insert or update would take a little more time. So it all depends on how many rows you are dealing with.
HTH
select columnname from dbo.TB
where CONVERT(varchar(20), columnname ) like '15'+'%'