SQL SUM function for a column with integer name - mysql

I'm trying to return the sum of a column with an integer name (for example, a column named 251)
CREATE FUNCTION myfun(cid varchar(255))
RETURNS float
BEGIN
RETURN (SELECT SUM(`cid`) FROM percentages);
END
I want it to return the sum of all the rows of the cid column of my percentages table, but it's instead returning the sum of the cids.
For example: if i call myfun(251)
if the values in the columns 251 are 1, 2, 3, and 4, I would expect an output of 1+2+3+4=10
But it is returning 251+251+251+251=1004 instead

You cannot do what you want with standard SQL. You simply cannot parameterize a column name, even with backticks, using regular SQL.
I can give you the hint that dynamic SQL (prepare/exec) can do what you want.
More importantly, I want to point out a flaw in your data model. You presumably have many columns with the same information. Instead, this data should be on separate rows. Something like:
id col val
SQL is usually better with more rows rather multiplying columns.

using dynamic sql and putting the column name in brackets, should work:
something like:
declare #sql varchar(255) = 'SELECT SUM([' + #cid + ']) FROM percentages'
exec(#sql)

Related

SSRS getting result from SQL Stored Procedure return value

Basically, I have a stored procedure that does some checking and will return a 1 or 0.
It is not a column, field name, something like
IF (some logic) RETURN 1
....
...
RETURN 0
I don't know what to put for the field source...
I am trying to make a text box show/hidden based on the result of the SP being a 1 or 0.
=IIF(something here, false, true)
Is there an issue with trying to get the return value to be used instead of columns?
Just do something like this in your dataset query
DECLARE #returnvalue int
EXEC #returnvalue = SP_SomeProc
SELECT #returnValue as TheResult
The dataset will return a field called TheResult containing the value from the SP
SSRS datasets expect to get column names from the query.
In your stored procedure use SELECT statements to return values.
IF (some logic) SELECT 1 as MyResult
...
SELECT 0 as MyResult
In your dataset properties, reference the procedure as usual and supply any parameters to it as needed. You should see the "MyResult" field under the dataset name.
In your expression you would check the result like this:
=IIf( First(Fields!MyResult.Value, "SP_Dataset") = 1, true, false )
If you were thinking of sending different values into the stored procedure in each cell with this expression, that will not work because each dataset is only evaluated once. If that is the desired behavior, you would need to check all the rows in the initial SQL instead.

Getting Scope error on SUM(IIF()) of rows from other table

Currently getting a scope error using the code below, what we're trying to do is count the number of rows that match the conditions we have:
=
SUM(
IIF(
Fields!Defect_Category.Value = "Packaging"
& Fields!Defect_Category.Value = "Major"
& Fields!WorkOrderDisplayID.Value = Fields!Work_Order_Id.Value,
1, 0),
"dsDefects"
)
Work_Order_Id is the "key" of the dsGeneral dataset which is the current scope/dataset of the tablix where we're trying to implement this. Any way we can fix this?
My understanding is that the Scope parameter of SUM is referring to the dataset we're trying to get the sum of (or count of, in this case). When I specify "dsDefects" as the scope of SUM, I get the following error:
The Value expression for the text box 'Textbox101' refers to the field
'Work_Order_Id'. Report item expressions can only refer to fields
within the current dataset scope or, if inside an aggregate, the
specified dataset scope. Letters in the names of fields must use the
correct case.
However, if I remove the scope parameter value, I'm getting the following error:
The Value expression for the text box 'Textbox101' refers to the field
'Defect_Category'. Report item expressions can only refer to fields
within the current dataset scope or, if inside an aggregate, the
specified dataset scope. Letters in the names of fields must use the
correct case.
I think you will need something like this...
=
IIF(
Fields!Defect_Category.Value = "Packaging"
& Fields!Defect_Category.Value = "Major",
LOOKUPSET(Fields!WorkOrderDisplayID.Value, Fields!WorkOrderDisplayID.Value, Fields!WorkOrderDisplayID.Value, "dsDefects").Length,
0)
I'll give a simple example of counting matches from another dataset and that might help you put the two together to get an solution.
If I create two datasets with the following queries, called dsEmp and dsDev respectively
dsEmp
DECLARE #e table (empid int, empname varchar(10))
insert into #e values
(1, 'Bob'), (2, 'Dave')
SELECT * FROM #e
dsDev
declare #d table(empid int, device varchar(10))
insert into #d VALUES
(1, 'Phone'),
(1, 'Laptop'),
(1, 'Desktop'),
(2, 'Phone'),
(3, 'Tablet')
SELECT * FROM #d
Then in my report I add a table bound to dsEmp showing the empID and empName and then in the final column I use the following expression
=LookupSet(
Fields!empid.Value,
Fields!empid.Value,
Fields!empid.Value, "dsDev"
).Length
I get this final output
As lookupset returns a collection, the collection's length is, in fact, the number of items contained in the collection.

Assigning multi value parameter (values) to a local parameter in sql query

I have a situation where I need to store the multi value parameter to a local parameter in sql query.
In the SSRS report builder I have created two multi value parameters #Customer and #LogisticsGroup
And in my SQL query , I have to assign those values to local parameters something like this
DECLARE #Acct NVARCHAR(100) , #LgstGroup NVARCHAR(MAX)
SELECT #Acct = (#Customer)
,#LgstGroup = (#LogisticsGroup)
But with this kind of approach I'm able to select only one value , if I select two values then the query is failing.
I tried this , but it seems like incorrect syntax.
DECLARE #Acct NVARCHAR(100) , #LgstGroup NVARCHAR(MAX)
SELECT #Acct IN (#Customer)
,#LgstGroup IN (#LogisticsGroup)
Please help to resolve this issue. Thanks much
You can store delimited values in a parameter. So for example, #Customer might have a string of ID's in it like 1,2,3. The report can concatenate the multiple values into this format using the Join function.
Now, in the SQL, one option is to use the LIKE operator to search the string. You could write something like:
#Customer like '%,' + Column_Name + ',%'
However, this approach is inefficient and you have to be careful of partial value matches.
A better approach is to create a user-defined table-valued function that can split the values and treat them like a table. There are plenty of examples out there, it's a pretty simple function. Then in practice it would look like this:
WHERE Column_Name in (select * from Split(#Customer))
OR
INNER JOIN Split(#Customer) ON...

Is there a way to set multiple values in one variable in SQL Server 2008?

I am a newbie in coding.
Here is what I did:
DECALRE #v VARCHAR(100)
SET #v = (SELECT TOP 100 NAMES FROM TestTable WITH(NOLOCK))
SELECT #v AS SampleData
But it returned an error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Are there any way to set multiple values in one variable in SQL Server 2008?
Thanks in advance.
There is one type of variable designed for holding multiple values. It's called a table variable:
declare #v table (Name varchar(100) not null)
insert into #v(Name )
select top 100 name from TestTable /* no ORDER BY means this is ill-defined */
You can insert/update/delete in this table variable and query from it via selects in exactly the same way as any other table.
Note though that this looks like you're breaking things down into "procedural" steps - first I'll get the top 100 names, then I'll do X, then I'll do Y. In SQL, you should try to put as much as possible into single queries, and leave it to the optimizer to determine what order to do things in, which subresults should be stored, etc.

How to count all NULL values in a table?

Just wondering, is there any quick way to count all the NULL values (from all columns) in a MySQL table?
Thanks for any idea!
If you want this done exclusively by MYSQL and without enumerating all of the columns take a look at this solution.
In this method you don't have to maintain the number of database columns by hard coding them. If your table schema will get modified this method will work, and won't require code change.
SET #db = 'testing'; -- database
SET #tb = 'fuzzysearch'; -- table
SET #x = ''; -- will hold the column names with ASCII method applied to retrieve the number of the first char
SET #numcolumns = 0; -- will hold the number of columns in the table
-- figure out how many columns we have
SELECT count(*) into #numcolumns FROM information_schema.columns where table_name=#tb and table_schema=#db;
-- we have to prepare some query from all columns of the table
SELECT group_concat(CONCAT('ASCII(',column_name,')') SEPARATOR ",") into #x from information_schema.columns where table_name=#tb and table_schema=#db;
-- after this query we have a variable separated with comma like
-- ASCII(col1),ASCII(col2),ASCII(col3)
-- we now generate a query to concat the columns using comma as separator (null values are omitted from concat)
-- then figgure out how many times the comma is in that substring (this is done by using length(value)-length(replace(value,',',''))
-- the number returned is how many non null columns we have in that column
-- then we deduct the number from the known number of columns, calculated previously
-- the +1 is added because there is no comma for single value
SET #s = CONCAT('SELECT #numcolumns - (length(CONCAT_WS(\',\',', #x, '))-length(replace(CONCAT_WS(\',\',', #x, '),\',\',\'\')) + 1) FROM ',#db,'.',#tb,';');
PREPARE stmt FROM #s;
EXECUTE stmt;
-- after this execution we have returned for each row the number of null columns
-- I will leave to you to add a sum() group call if you want to find the null values for the whole table
DEALLOCATE PREPARE stmt;
The ASCII is used to avoid reading, concatenating very long columns for nothing, also ASCII makes us safe for values where the first char is a comma(,).
Since you are working with reports, you may find this helpful as this can be reused for each table if you put in a method.
I tried to let as many comments as possible.
Let's split on pieces the above compact way (reverse way):
I wanted to end up having a query like this
SELECT totalcolumns - notnullcolumns from table; -- to return null columns for each row
While the first one is easy to calcule by running:
SELECT count(*) FROM information_schema.columns where table_name=#tb and table_schema=#db;
The second one the notnullcolumns is a bit of pain.
After a piece of examination of the functions available in MySQL, we detect that CONCAT_WS does not CONCAT null values
So running a query like this:
SELECT CONCAT_WS(",","First name",NULL,"Last Name");
returns: 'First name,Last Name'
This is good, we take rid of the null values from the enumeration.
But how do we get how many columns were actually concatenated?
Well that is tricky. We have to calculate the number of commas+1 to get the actually concatenated columns.
For this trick we used the following SQL notation
select length(value)-length(replace(value,',','')) +1 from table
Ok, so we have now the number of concatenated columns.
But the harder part is coming next.
We have to enumerate for CONCAT_WS() all values.
We need to have something like this:
SELECT CONCAT_WS(",",col1,col2,col3,col4,col5);
This is where we have to take use of the prepared statements, as we have to prepare an SQL query dynamically from yet unknown columns. We don't know how many columns will be in our table.
So for this we use data from information_schema columns table. We need to pass the table name, but also the database name, as we might have the same table name in separate databases.
We need a query that returns col1,col2,col3,col4,col5 to us on the CONCAT_WS "string"
So for this we run a query
SELECT group_concat(column_name SEPARATOR ",") into #x from information_schema.columns where table_name=#tb and table_schema=#db;
One more thing to mention. When we used the length() and replace() method to find out how many columns were concatenated, we have to make sure we do not have commas among the values. But also take note that we can have really long values in our database cells. For both of this trick we use method ASCII('value'), which will return the ASCII char of the first char, which cannot be comma and will return null for null columns.
That being said we can compact all this in the above comprehensive solution.
Something like
select id
, sum ( case when col1 is null then 1 else 0 end case ) col1
, sum ( case when col2 is null then 1 else 0 end case ) col2
, sum ( case when col3 is null then 1 else 0 end case ) col3
from contacts
group by id
Something like this (substitute COL_COUNT as appropriate):
select count(*) * COL_COUNT - count(col1) - count(col2) - ... - count(col_n) from table;
You should really do this using not only SQL, but the language which is at your disposal:
Obtain the metadata of each table - either using DESCRIBE table, or using a built-in metadata functionality in your db access technology
Create queries of the following type in a loop for each column. (in pseudo-code)
int nulls = 0;
for (String colmnName : columNames) {
query = "SELECT COUNT(*) FROM tableName WHERE " + columnName + " IS NULL";
Result result = executeQuery(query);
nulls += result.size();
}