Generate, then order random numbers in SQL - mysql

So I'm using SQL to build a database of math questions.
My typical query might look something like this:
SELECT #num1 := FLOOR(Rand()*8 + 2),#num2 := FLOOR(Rand()*8 + 2);
INSERT INTO `QuestionDB`(TopicID, TopicName, SubtopicID, SubtopicName, Question, Answer, Difficulty, Author, Projectable) VALUES (3,"Multiplying",1,"Multiplying single digit numbers",CONCAT(#num1, " × ", #num2 ,"= "),#num1*#num2,1,"Me","Yes");
I'm using PHPMyAdmin to do this and build up my database.
This works fine. However, what I would like to do is this, so that I can order numbers ie:
SELECT #num1 := FLOOR(Rand()),#num2 := FLOOR(Rand()),#num3 := FLOOR(Rand()),#num4 := FLOOR(Rand());
INSERT INTO `QuestionDB`(TopicID, TopicName, SubtopicID, SubtopicName, Question, Answer, Difficulty, Author, Projectable) VALUES (7,"Ordering",1,"Ordering whole numbers",CONCAT("<span class='smaller'>Order, from highest to lowest</span><br>", #num1, " , ", #num2, etc),ORDER_ASS(#num1,#num2,#num3,#num4),1,"Me","Yes");
However, obviously ORDER_ASS isn't a valid SQL function. Is there an SQL function that will do this?

Related

Why is the integer output quoted in MySQL custom variable

SELECT score ,TRIM(BOTH "" FROM ink) as `rank` FROM (
SELECT
score,
#r :=
IF (#p = score, #r, #r + 1) AS ink,
#p := score
FROM
scores,
(SELECT #r := 0, #p := NULL) init
ORDER BY
score DESC)dep
print{"headers": ["score", "rank"], "values": [[4.00, "1"], [4.00, "1"], [3.85, "2"], [3.65, "3"], [3.65, "3"], [3.50, "4"]]}
79/5000
I made a custom integer variable in MySQL. After I output, there were more quotes than the title, and then I used a function to remove the quotes, but the output result was still there. This situation makes me confused, I hope someone can answer it. Thank you.
TRIM() output datatype is string. So double quote chars are not a part of the value. They are added by PHP while dumping the value for to mark that the datatype is string.
If you want to obtain the output column of numeric datatype (and in this case PHP will not add dquotes) use ink + 0 or CAST(ink AS UNSIGNED).
DEMO fiddle. Pay attention to the value in column adjustment - strings are left-justified, numbers are right-justified.
PS. If you alter initial value for #p from NULL to some value which cannot be present in score (for example, -1, '' and so on) then #r := IF(#p = score, #r, #r + 1) may be simplified to #r := #r + (#p = score).
--
I was wondering why I defined #r=0, shouldn't it be an integer, but the output value is in quotes – johnson
This is user-defined variables processing feature.
Server cannot predict the value of what datatype will be assigned to the variable in future (in your query - while next row will be processing). So it uses the most common datatype for user-defined variable, the datatype which can accept the value of absolutely any datatype. And this the most common datatype is binary string (more precisely - LONGBLOB).
Additionally - all user-defined variables values are stored in PERFORMANCE_SCHEMA.user_variables_by_thread table. And according column datatype is LONGBLOB.

SSRS Report Parameters passed out

I am currently building a number of logging and analysis tools to keep tabs on our SQL environment. We are currently using SQL Server 2014.
What I want to do is keep check of all the parameters that are passed to our reports during the day. All of the reports are currently using stored procedures so in my table or a select statement based on a table is output the stored procedure with the parameters for every time the report was run.
At the end of the day I would then like to be able to take the outputted statement and run it in SSMS without having to use the report. I have been looking at the ExceutionLogStorage table and the ExecutionLog view's and though it has most of the information that I need, the parameters are not in an easily usable state.
Has anyone done something similar to what I have described?
You need to add logging part in your original SP, for example:
Alter procedure a
(#parameter)
As
Begin
..
..
Insert into loggingTable(col)
Values(#parameter)
..
..
End
Then query directly against that loggingTable for getting the history of used parameters
A Google search around this topic quickly brought up the following blog post already identified by the OP as useful and shown below (this query itself is actually an expansion of work linked to by LONG's answer below)
SELECT TOP 1 ParValue
FROM (
SELECT els.TimeEnd
, IIF(CHARINDEX('&' + 'ParameterName' + '=', ParsString) = 0, 'ParameterName',
SUBSTRING(ParsString
, StartIndex
, CHARINDEX('&', ParsString, StartIndex) - StartIndex)) AS ParValue
FROM (SELECT ReportID, TimeEnd
, '&' + CONVERT(VARCHAR(MAX), Parameters) + '&' AS ParsString
, CHARINDEX('&' + 'ParameterName' + '=', '&' + CONVERT(VARCHAR(MAX), Parameters) + '&')
+ LEN('&' + 'ParameterName' + '=') AS StartIndex
FROM ExecutionLogStorage
WHERE UserName='UserName' -- e.g. DOMAIN\Joe_Smith
) AS els
INNER JOIN [Catalog] AS c ON c.ItemID = els.ReportID
WHERE c.Name = 'ReportName'
UNION ALL
SELECT CAST('2000-01-01' AS DateTime), 'ParameterName'
) i
ORDER BY TimeEnd DESC;
Both these approaches though really only give us a starting point since they (variously) rely upon us knowing in advance the report name and parameter names. Whilst we can quickly make a couple of changes to Ken Bowman's work to get it to run against all executions of all reports, we still have the problem that the query hardcodes the parameter name.
The parameters required to execute a report are stored on the Catalog table in the Parameter column. Although the column has a datatype ntext, it is actually storing an XML string. Meaning we can use an XPath query to get at the parameter names
with
CatalogData as (
select ItemID, [Path], [Name], cast(Parameter as xml) 'ParameterXml'
from Catalog
where [Type] = 2),
ReportParameters as (
select ItemID, [Path], [Name], ParameterXml, p.value('Name[1]', 'nvarchar(256)') 'ParameterName'
from CatalogData
cross apply ParameterXml.nodes('/Parameters/Parameter') as Parameters(p))
select *
from ReportParameters;
Executing this query will list all reports on the server and their parameters. Now we just need to combine this with Ken Bowman's query. I've gone with a CTE approach
with
CatalogData as (
select ItemID, [Path], [Name], cast(Parameter as xml) 'ParameterXml'
from Catalog
where [Type] = 2),
ReportParameters as (
select ItemID, [Path], [Name], p.value('Name[1]', 'nvarchar(256)') 'ParameterName'
from CatalogData
cross apply ParameterXml.nodes('/Parameters/Parameter') as Parameters(p))
select
els.TimeEnd
, c.[Name]
, rp.ParameterName
, iif(
charindex(
'&' + rp.ParameterName + '=', ParametersString) = 0
, rp.ParameterName, substring(ParametersString
, StartIndex, charindex('&', ParametersString, StartIndex) - StartIndex
)) 'ParameterValue'
from (
select
ReportID
, TimeEnd
, rp.ParameterName
, '&' + convert(varchar(max), Parameters) + '&' 'ParametersString'
, charindex(
'&' + rp.ParameterName + '=',
'&' + convert(varchar(max), Parameters) + '&'
) + len('&' + rp.ParameterName + '=') 'StartIndex'
from
ExecutionLogStorage
inner join ReportParameters rp on rp.ItemID = ReportID) AS els
inner join [Catalog] c on c.ItemID = els.ReportID
inner join ReportParameters rp on rp.ItemID = c.ItemID and rp.ParameterName = els.ParameterName;
Note that the parameter values are passed to the report as part of a URL, so you'll still need get rid the literal space encoding and so on. Also, this doesn't (yet...) work for multi-value parameters.

Prevent SQL injection

Question 1:
I have the below MySQL query which works fine but I've just discovered this is not a safe approach as its open to SQL injection. As you can see the where clause is an issue if I wanted to pass as an argument.
_, err := dbmap.Select(&response.AppsData, "SELECT...", ?)
Any advice much appriciated.
where := ""
for i := 0; i < (len(acl_user_apps)); i++ {
fmt.Println(acl_user_apps[i].AppId)
fmt.Println(acl_user_apps[i].Permissions)
if where == "" {
where = "WHERE Apps.id=" + strconv.Itoa(acl_user_apps[i].AppId)
} else {
where = where + " OR Apps.id=" + strconv.Itoa(acl_user_apps[i].AppId)
}
}
query := "SELECT Apps.*, GROUP_CONCAT(DISTINCT IFNULL(AppCategoryMatches.category_id,'-1') SEPARATOR ',') as temp, GROUP_CONCAT(DISTINCT IFNULL(AppCategories.category_name,'-1') SEPARATOR ',') as tmp_name FROM Apps LEFT JOIN AppCategoryMatches ON AppCategoryMatches.app_id=Apps.id LEFT JOIN AppCategories ON (AppCategoryMatches.`category_id` = AppCategories.id) " + where + " GROUP BY Apps.id ORDER BY " + sort_by + " " + order_by + " LIMIT " + limit + " OFFSET " + offset)
_, err := dbmap.Select(&response.AppsData,query)
Question 2: Also just wondering if anyone has ever had issues passing ORDER argument...
_, err := dbmap.Select(&response.AppsData,
"SELECT Apps.*, GROUP_CONCAT(DISTINCT IFNULL(AppCategoryMatches.category_id,'-1') SEPARATOR ',') as temp, GROUP_CONCAT(DISTINCT IFNULL(AppCategories.category_name,'-1') SEPARATOR ',') as tmp_name FROM Apps LEFT JOIN AppCategoryMatches ON AppCategoryMatches.app_id=Apps.id LEFT JOIN AppCategories ON (AppCategoryMatches.category_id = AppCategories.id) GROUP BY Apps.id ORDER BY ?", "title")
This ORDER is the simplest thing ever... why isnt it working?
You absolutely don't want to be "escaping" any strings on your own, nor concatenating strings to make queries.
Go's database/sql (http://golang.org/pkg/database/sql/) package supports parameterised queries by default - e.g. db.Query("SELECT * FROM users WHERE id=? AND active=?", id, userStatus) - where ? acts as a placeholder for mySQL to handle your variables.
You can (in combination with parameterised queries) use a query builder like mgutz/dat that can help if you're not great at writing raw SQL. A package like that or sqlx also helps pack/unpack queries to/from structs or maps in your application.
There's also a great guide in this tutorial for using Go's database package. I highly suggest reading it.
I don't know Go language. But most of languages have function for escaping strings (PHP example: http://php.net/manual/en/function.mysql-real-escape-string.php). If you're inserting only integers to your query, you only need to convert values from string to int.
Check this out: http://astaxie.gitbooks.io/build-web-application-with-golang/content/en/09.4.html maybe you'll find some answers.
And about your ORDER - can you put here complete sql query that you're calling?

How to format int to price format in SQL?

I select the price 1000000 and I need to format it to $1,000,000. How can I do that in SQL?
To format with commas, you can use CONVERT with a style of 1:
declare #money money = 1000000
select '$' + convert(varchar, #money, 1)
will produce $1,000,000.00
If you want to remove the last 3 characters:
select '$' + left(convert(varchar, #money, 1), charindex('.', convert(varchar, #money, 1)) - 1)
and if you want to round rather than truncate:
select '$' + left(convert(varchar, #money + $0.50, 1), charindex('.', convert(varchar, #money, 1)) - 1)
Creating Function:
CREATE FUNCTION [dbo].[f_FormatMoneyValue]
(
#MoneyValue money
)
RETURNS VARCHAR(50)
AS
BEGIN
RETURN cast(#MoneyValue as numeric(36,2))
END
Using in Select Query:
Select dbo.f_FormatMoneyValue(isnull(SalesPrice,0))SalesPrice from SalesOrder
Output:
100.00
Formatting Money Value with '$' sign:
CREATE FUNCTION [dbo].[f_FormatMoneyWithDollar]
(
#MoneyValue money
)
RETURNS VARCHAR(50)
AS
BEGIN
RETURN '$' + convert(varchar, #MoneyValue, 1)
END
Output:
$100.00
Note: The above sample is for the money field. You can modify this function according to your needs
Hope this helps you..! :D
SELECT FORMAT(price, 'C2', 'en-us')
The SQL Server money datatype is just decimal(10, 4). To my knowledge there is no datatype that will present the way you want.
Adding the dollar sign and commas is something that should belong in the application logic, but if you really must do it through a database object consider adding the dollar sign, and commas every three characters (after the decimal point). In other words, you'll have to convert the int to varchar and do string manipulation.
It depends, however, there's no simple way to do it in standard SQL specs(SQL-92, SQL-2003, etc.).
For PostgreSQL PL/pgSQL and Oracle PL/SQL, you can use to_char to format numbers:
select to_char(1234567.123, 'FM$999,999,999.99')
Which gives output:
$1,234,567.12
See: http://www.postgresql.org/docs/7/static/functions2976.htm

SQL Server Agent - Can Job Ask Information About Itself

I don't suppose anyone knows whether a SQL Server Agent Job can ask information about itself, such as its own ID, or the path it's running from? I'm aware of xp_sqlagent_enum_jobs and sp_help_job but this doesn't help, because you have to specify the job ID.
The idea is that we want code that we don't have to manage by being able to call a sproc which will identify the current job. Any ideas?
Yes, but it isn't pretty.
Look at the sys.sysprocesses (or dbo.sysprocesses in SQL 2000 and below). The program name will be SQL Agent something with a binary value at the end. That binary value is the binary vale of the guid of the job. So, substring out that value and do a lookup against the msdb.dbo.sysjobs table to find out what job it is (you'll need to cast the sysjobs.job_id to varbinary(100) to get the values to match).
I told you it wasn't pretty, but it will work.
nasty!!! but i think it might work...
eg. used within a job - select * from msdb..sysjobs where job_id = dbo.fn_currentJobId()
let me know.
create function dbo.fn_CurrentJobId()
returns uniqueidentifier
as
begin
declare #jobId uniqueidentifier
select #jobId = j.job_id
from master..sysprocesses s (nolock)
join msdb..sysjobs j (nolock)
on (j.job_id = SUBSTRING(s.program_name,38,2) + SUBSTRING(s.program_name,36,2) + SUBSTRING(s.program_name,34,2) + SUBSTRING(s.program_name,32,2) + '-' + SUBSTRING(s.program_name,42,2) + SUBSTRING(s.program_name,40,2) + '-' + SUBSTRING(s.program_name,46,2) + SUBSTRING(s.program_name,44,2) + '-' + SUBSTRING(s.program_name,48,4) + '-' + SUBSTRING(s.program_name,52,12) )
where s.spid = ##spid
return #jobId
end
go
thanks for the info though