Build sql query based on input param - ruby

I am building a SQL query and created a execution method to run it as follows:
module Helper
module Action
BASE_SQL_QUERY = 'SELECT a,b,c FROM SOME_TABLE'
SELECT_QUERY = "#{BASE_SQL_QUERY}"
def self.execute(action:, db_client:, data:)
db_client.prepare(Helper::Count::SELECT_QUERY).execute
end
end
end
It is working fine bit I am going to have exact same module and the only different will be in the query. Instead SELECT a,b,c it'll be SELECT count(*) and everything else will be same.
In execution method action argument will have what action i want to do action == 'read' do SELECT a,b,c and for action == count do SELECT count(*).
What am trying to do is have only one module Action and based on action value build sql. Is it possible to do that? I tried bunch of ways like creating a method and passing action to it and tried to build sql but i get error dynamic constant assignment because CONSTANT value can not be in method.
is it possible to build sql based on action value?

Sure it's possible, any existing ORM does exactly that among other things - provide the interface to build sql queries using the language common syntax.
Besides large and scary full-featured ORMs there are some gems that does just the job you're looking for - query building. You could check how they are implemented and borrow some ideas there (for example, https://github.com/izniburak/qruby)

Related

Is there a way to create a SQVI query in SAP with a complex conditional "WHERE" clause?

I am trying to create an SAP data query using SQVI (or SQ01) to display all entries that meet certain criteria. I can use the 'Selection Fields' tab to have a user specify any of these parameters, but I want to be able to query the data with more complex conditions such as a nested 'AND'/'OR'. I have researched the question for a couple hours and have yet to find a solution that works. Here is an example simplified query that I would like to do, written in SQL form:
SELECT t0.name, t0.birthYear, t1.grade, t1.county
FROM t0
INNER JOIN t1 on t0.personID = t1.personID
WHERE t0.name = 'Bob'
AND t0.birthyear = 2000
AND (t1.grade = 12
OR t1.county <> 'Cook');
Now the tricky part is figuring out how to do a nested 'AND' and 'OR' in SQVI. At first, I pulled all the data without these conditions, exported it to Excel, and then performed this logic to get the correct entries that meet these criteria. However, I do not want to do this every time, as it is highly repetitive and there HAS TO be some solution within the SAP environment. Ideally, I would be able to create a query that I can share with co-workers to execute once a week, where they don't need to enter any values to test against 'name', 'birthyear', 'grade', or 'county'. They should be able to type in the code for this query and hit execute, and it should spit out all of the entries that meet all the criteria. I want to be able to hard-code the testing parameters in this instance.
Let me know if this is even possible! If it's not possible using SQVI, what would I need access to in order to do a complex conditional query like this? I do not have write-access on the data, so I am not authorized to use 'DBACOCKPIT' to write the query as SQL (which would be so much simpler).

Can I use expression builder to return a SQL query result to a variable?

I am using a software, pc/mrp, which appears to have a built-in Visual Fox Pro editor for FRX files. It also has an external usage of an ef file. Based on some usage of Google, the report designer seems standard, not custom. The ef file usage may be a custom thing. Now, I need to find a way to get access to a value from a SQL statement inside the report. The statement needs to run per-line in the report.
EF:
This file has sections:
~in~
~out~
In these sections, I can run code, but if there is a ~perline~ type section, I don't know how to access it. I can use the ~in~ to try to create a relationship between the databases, as shown in the following example:
~IN~
THISAREA = SELECT()
USE PARTMAST ORDER BYPARTNO IN 0
SELECT (THISAREA)
SET RELATION TO PARTNO INTO PARTMAST ADDITIVE
GO TOP
~OUT~
USE IN SELECT("SALES")
But, for this I don't know how to join the databases. I have two databases (A,B) I need to connect them based on two fields (pono,line). If (A.pono and a.line) = (B.pono and B.line) then they would be linked. Is this possible?
Report Designer:
The other way I see this working is to do the query inside the report designer. Inside report properties is a variable tab. I can use this to assign to variables using expressions. I need:
SELECT field from B where B.pono = pono and B.line = line; INTO ARRAY varArray;
But, it gives me an error, likely because this is trying to create a new variable as opposed to actually assigning to the variable in the report. I tried editing a field inside the designer to use the preceeding code as well, but that also failed.
Is there a way using the report designer or the ef file to grab the data I need per line?
The sample code you show is doing something like a join with the SET RELATION command. To use SET RELATION, there has to be an index on the relevant field (expression) in the child table. So, if your table B has an index on PONO + LINE (or, if those are numeric, STR(PONO, length) + STR(LINE, length)), you can SET RELATION TO PONO + LINE INTO B, again, using the more complicated expression if necessary.

EF core 2 first query slow

I'm using EF core 2 as ORM in my project.
I faced this problem while executing this query:
var query = (from droitsGeo in _entities.DroitsGeos
join building in _entities.Batiments
on droitsGeo.IdPerimetre equals building.IdBatiment
where droitsGeo.IdUtilisateur == idUser &&
droitsGeo.IdClient == idClient &&
building.Valide == true &&
droitsGeo.IdNiveauPerimetre == geographicalLevel
orderby sort ascending
select new GeographicalModel
{
Id = building.IdBatiment,
IdParent = building.IdEtablissement,
Label = building.LibBatiment,
});
First execution tooks about 5 second and second less than one second as show below :
First execution of query :
Time elapsed EF: 00:00:04.8562419
After first execution of query :
Time elapsed EF: 00:00:00.5496862
Time elapsed EF: 00:00:00.6658079
Time elapsed EF: 00:00:00.6176030
I have same result using Stored procedure.
When i execute sql query generated by EF in SQL Server, the result is returned in less than a second.
what is wrong with EF Core 2 or did i miss something in configuration?
The EF by default tracks all the entities you run queries against.
When you run it for the first time the track change mechanism kicks in... that's why it takes a little bit longer.
You can avoid this, especially when retrieving collections by using .AsNoTracking() when composing the query.
Take a look:
var items = DbContext.MyDbSet
.Include(SecondObject)
.AsNoTracking()
.ToList();
EF core needs to compile LINQ quires using reflection therefor first queries are always slow. There is already a GitHub issue here
I have a simple idea to resolve this issue with the help of stored procedures and thereafter AutoMapper.
Create an stored procedures that return all the columns that you want, no matter if they are from different tables. Once the data is received from the stored procedure and you have received the object in one of your Model classes, you can then use AutoMapper to map only the relevant attributes to other classes. Please note that I am not giving you a tutorial of how to use stored procedure. I am giving you an example that might explain better:
A stored procedure is created which returns results from three tables named A, B and C.
A model class named SP_Result.cs is created corresponding to created stored procedure to map the received object of stored procedure (this is required when working with stored procedures in EF Core)
'ViewModels` are created having same attributes as returning from each table A, B and C.
Thereafter, mapping configurations will be created for SP_Result with ViewModel of Class A, Class B and Class C. e.g. CreateMap<SP_Result, ViewModel_A>(); CreateMap<SP_Result, ViewModel_B>();. I suppose, you would have a request and response objects which can be used instead of ViewModels. Name the properties accordingly in the stored procedure using AS keyword. e.g. Select std_Name AS 'Name'
This mapping will map the individual properties to each class. AutoMapper ignore the properties which do not exists in either of the classes mentioned in Mapping Configuration.
If you are selecting a list of objects where each object does have its own list of objects, this scenario will generally create N + 1 queries in EF. In fact, if you try to achieve this using stored procedures, you will have to create multiple queries or run the stored procedure multiple times (in a loop may be), or you will end up receiving Cartesian product.

LINQ and Generated sql

suppose my LINQ query is like
var qry = from c in nwEntitiesContext.CategorySet.AsEnumerable()
let products = this.GetProducts().WithCategoryID(c.CategoryID)
select new Model.Category
{
ID = c.CategoryID,
Name = c.CategoryName,
Products = new Model.LazyList<Core.Model.Product>(products)
};
return qry.AsQueryable();
i just want to know what query it will generate at runtime....how to see what query it is generating from VS2010 IDE when we run the code in debug mode....guide me step by step.
There is not much to see here - it will just select all fields from the Category table since you call AsEnumerable thus fetching all the data from the Category table into memory. After that you are in object space. Well, depending on what this.GetProducts() does - and my guess it makes another EF query fetching the results into memory. If that's the case, I would strongly recommend you to post another question with this code and the code of your GetProducts method so that we can take a look and rewrite this in a more optimal way. (Apart from this, you are projecting onto a mapped entity Model.Category which again won't (and should not) work with Linq-to-Entities.)
Before reading into your query I was going to recommend doing something like this:
string sqlQueryString = ((ObjectQuery)qry).ToTraceString();
But that won't work since you are mixing Linq-to-Entities with Linq-to-objects and you will actually have several queries executed in case GetProducts queries EF. You can separate the part with your EF query and see the SQL like this though:
string sqlString = nwEntitiesContext.CategorySet.ToTraceString();
but as I mentioned earlier - that would just select everything from the Categories table.
In your case (unless you rewrite your code in a drastic way), you actually want to see what queries are run against the DB when you execute the code and enumerate the results of the queries. See this question:
exact sql query executed by Entity Framework
Your choices are SQL Server Profiler and Entity Framework Profiler. You can also try out LinqPad, but in general I still recommend you to describe what your queries are doing in more detail (and most probably rewrite them in a more optimal way before proceeding).
Try Linqpad
This will produce SELECT * FROM Categories. Nothing more. Once you call AsEnumerable you are in Linq-to-objects and there is no way to get back to Linq-to-entities (AsQueryable doesn't do that).
If you want to see what query is generated use SQL Profiler or any method described in this article.

Ruby w/ Postgres & Sinatra - Query won't order right with parameter?

So I set a variable in my main ruby file that's handling all my post and get requests and then use ERB templates to actually show the pages. I pass the database handler itself into the erb templates, and then run a query in the template to get all (for this example) grants.
In my main ruby file:
grants_main_order = "id_num"
get '/grants' do
erb :grants, :locals => {:db=>db, :order=>grants_main_order, :message=>params[:message]}
end
In the erb template:
db = locals[:db]
getGrants = db.exec("SELECT * FROM grants ORDER BY $1", [locals[:order]])
This produces some very random ordering, however if I replace the $1 with id_num, it works as it should.
Is this a typing issue? How can I fix this? Using string replacement with #{locals[:order]} also gives funky results.
Parameters are there to put in constant values into the query. It's possible and legal, but not meaningful to use them in an ORDER BY-clause.
Say you want to issue this query:
SELECT first_name, last_name
FROM people
ORDER BY first_name
If you put "first_name" in a string and pass it in as a parameter, you instead get:
SELECT first_name, last_name
FROM people
ORDER BY "first_name"
The difference is huge. That last ORDER BY-clause really tells te database not to care about the column values for each row, and just sort as if all rows were identical. Sorting order will be random.
I would recommend using datamapper (http://datamapper.org/) for sinatra. It's a very slick ORM and handles the paramaterized queries you are trying to build quite well.
have you inspected what locals[:order] is? Maybe something funky in there.
p locals[:order]

Resources