This is meant as kind of a simplified / more to the point question of:
NHibernate / QueryOver: How to left join with parameter
The core problem is that I have the following query:
1)
Select v.*, ...
from someView v
LEFT JOIN someTable t on v.ForeignKey = t.ForeignKey
AND t.SomeOtherValue = #myParam
where #myParam is some parameter.
I want to use this query inside a view but since I don´t know #myParam when creating the view I don´t know any way to attach it to the query so it is used directly inside the join. All I can do is get a version of the query like this:
2)
Select v.*, ...
from (someView v
LEFT JOIN someTable t ON v.ForeignKey = t.ForeignKey)
WHERE SomeOtherValue = 123
wich would in the view it would look like this:
3)
CREATE VIEW myView AS
Select v.*, t.SomeOtherValue, ...
from someView v
LEFT JOIN someTable t on v.ForeignKey = t.ForeignKey
and then say:
SELECT *
from myView
where SomeOtherValue = #myParam
In both cases (2 and 3) #myParam gets applied only after the left join already happened, so the result set is different (and in my case incorrect).
So I am searching for a way to rewrite 1) in a way so that I can use it inside a view (with similar syntax as in 2 and 3)
NOTE:
Using a table valued function with #myParam as parameter would work but then again I can´t use it as a model for NHibernate or with QueryOver, so that is not really an option.
On DB2, I accomplished what you're trying to do once by:
Making a scalar UDF function that was referenced in place of your #myParam.
The scalar UDF function retrieved the parameter value from a SESSION (temporary table).
Prior to referencing the view at runtime, my code created or recreated and populated the SESSION temporary table with a single row with the parameter value (giving the scalar UDF something to feed off of).
At runtime, the SQL view reference would call the scalar UDF function, which would access the temp table, and return the parameter value to the view, and voila, it worked.
On DB2, a table UDF function could perform in a similar manner. In general, table functions are more flexible than scalar functions, and are often a better choice.
DB2 supports late binding when using functions in that manner.
I don't know what database you're using, but you might have good luck trying something similar.
Related
I do not fully understand the "USE" statement in Transact-SQL and how it affects the scope of temp tables. I have a user-defined table type in one database but not another, and I've found I need to "USE" that database in order to define a table of that type. Earlier in the query, I define a temporary table. After the "USE" statement, SSMS does not recognize the temp table as a valid object name, however I can still query from it without error.
The skeleton of my SQL query is as follows:
USE MYDATABASE1
[... a bunch of code I did not write...]
SELECT * INTO #TEMP_TABLE FROM #SOME_EARLIER_TEMP_TABLE
USE MYDATABASE2
DECLARE #MYTABLE MyUserDefinedTableType -- this table type only exists in MYDATABASE2
INSERT INTO #MYTABLE(Col1, Col2)
SELECT Col1, Col2 FROM (SELECT * FROM MYDATABASE2.dbo.SOME_TABLE_VALUED_FUNCTION(param1, param2)) T
SELECT A.*, B.Col2
FROM #TEMP_TABLE A
CROSS APPLY DATABASE2.dbo.SOME_OTHER_TABLE_VALUED_FUNCTION(#MYTABLE, A.SomeColumn) B
In the last SELECT statement, SSMS has red squiggly lines under "A.*" and "#TEMP_TABLE", however there is no error running the query.
So my question is: am I doing something "wrong" even though my query still works? Assuming the initial "USE MYDATABASE1" is necessary, what is the correct way to switch databases while still having #TEMP_TABLE available as a valid object name? (Note that moving the definition of #TEMP_TABLE to after "USE MYDATABASE2" would just shift the problem to #SOME_EARLIER_TEMP_TABLE.)
In SQL USE basically tells the query which database is the "default" database.
Temp tables can play tricks on intellisense - unless they're explicitly defined using the CREATE TABLE #MyTempTable route, intellisense doesn't really know what to do with them a lot of the time. Don't worry though - temp tables are scoped to the query.
Although I do feel it's worth pointing out: while UDTs are database specific, you can create an assembly to use across databases
I have lots of experience with T-SQL (MS SQL Server).
There it is quite common to first select some set of records into a
table variable or say temp table t, and then work with this t
throughout the whole SP body using it just like a regular table
(for JOINS, sub-queries, etc.).
Now I am trying the same thing in Oracle but it's a pain.
I get errors all the way and it keeps saying
that it does not recognize my table (i.e. my table variable).
Error(28,7): PL/SQL: SQL Statement ignored
Error(30,28): PL/SQL: ORA-00942: table or view does not exist
I start thinking what at all is possible to do with this
table variable and what not (in the SP body) ?
I have this declaration:
TYPE V_CAMPAIGN_TYPE IS TABLE OF V_CAMPAIGN%ROWTYPE;
tc V_CAMPAIGN_TYPE;
What on Earth can I do with this tc now in my SP?!
This is what I am trying to do in the body of the SP.
UPDATE ( SELECT t1.STATUS_ID, t2.CAMPAIGN_ID
FROM V_CAMPAIGN t1
INNER JOIN tc t2 ON t1.CAMPAIGN_ID = t2.CAMPAIGN_ID
) z
SET z.STATUS_ID = 4;
V_CAMPAIGN is a DB view, tc is my table variable
Presumably you are trying to update a subset of the V_CAMPAIGN records.
While in SQLServer it may be useful to define a 'temporary' table containing the subset and then operate on that it isn't necessary in Oracle.
Simply update the table with the where clause you would have used to define the temp table.
E.g.
UPDATE v_campaign z
SET z.status_id = 4
WHERE z.column_name = 'a value'
AND z.status <> 4
I assume that the technique you are familiar with is to minimise the effect of read locks that are taken while selecting the data.
Oracle uses a different locking strategy so the technique is mostly unnecessary.
Echoing a comment above - tell us what you want to achieve in Oracle and you will get suggestions for the best way forward.
Using Postgres 9.3, I found out that I can perform something like this:
SELECT generate_series(1,10);
But I can't do this:
SELECT (SELECT generate_series(1,10));
Can I somehow cast SELECT result to setof int to use it same as result from generate_series()?
What exactly is happening there why I can use result from function but not from SELECT?
Your first form is a non-standard feature of Postgres. It allows SRF (Set Returning Functions) in the SELECT list, which are expanded to multiple rows:
Is there something like a zip() function in PostgreSQL that combines two arrays?
Note: that's working for functions, not for sub-selects. That's why your second SELECT is simply invalid syntax.
Standard SQL does not have a provision for that at all, so the feature is frowned upon by some and clean alternatives have been provided (thanks to improvements in the SQL standard). It is largely superseded by the LATERAL feature in Postgres 9.3+:
What is the difference between LATERAL and a subquery in PostgreSQL?
The simple form can be replaced by:
SELECT g
FROM generate_series(1,10) g;
Whenever possible move SRF to the FROM clause and treat them like tables - since version 9.3 that's almost always possible.
Note that g serves as table and column alias automatically in the example. g in SELECT g binds to a column name first. More explicit syntax:
SELECT g
FROM generate_series(1,10) AS t(g); -- table_alias(column_alias)
You need to understand the difference between a row, a set of rows (~ a table) and an array. This would give you an array of integer:
SELECT ARRAY(SELECT g FROM generate_series(1,10) g) AS g_arr;
Browse the tags generate-series and set-returning-functions for many related answers with code examples.
I have a column inside my SQL Server 2012 table which contains following Json data.
[{"bvin":"145a7170ec1247cfa077257e236fad69","id":"b06f6aa5ecd84be3aab27559daffc3a4"}]
Now I want to use this column data in my query like
select *
from tb1
left join tb2 on tb1.(this bvin inside my column) = tb2.bvin.
Is there a way to query JSON data in SQL Server 2012?
Honestly, this is a terrible architecture for storing the data, and can result in some serious performance issues.
If you truly don't have control to change the database, you can accomplish this by parsing out the value with SUBSTRING like below, but it's leading down a very unhappy path:
SELECT *
FROM tb1
JOIN tb2 on tb2.bvin =
SUBSTRING(
tb1.json
,CHARINDEX('"bvin":"', tb1.json) + LEN('"bvin":"')
,CHARINDEX('"', tb1.json, CHARINDEX('"bvin":"', tb1.json) + LEN('"bvin":"')) - CHARINDEX('"bvin":"', tb1.json) - LEN('"bvin":"')
)
And sadly, that's as easy as it can be.
Another solution is JSON Select which providers a JsonNVarChar450() function. Your example would be solved like so:
select *
from tb1
left join tb2 on dbo.JsonNVarChar450(tb1.YourColumnName, 'bvin') = tb2.bvin
as someone mentioned, this could be a bit slow, however you could add an index using the JSON Select function like so:
alter table tb2 add
bvin as dbo.JsonNVarChar450(YourColumnName, 'bvin') persisted
go
create index IX_tb2_bvin on tb2(bvin)
And from then on you can query using the index over the computed column bvin, like so:
select *
from tb1
left join tb2 on tb1.bvin = tb2.bvin
DISCLOSURE:
I am the author of JSON Select, and as such have an interest in you using it :)
Please vote for the feature here. In workaround section there you can find links to function-based solutions: http://www.sqlservercentral.com/articles/JSON/68128/ and https://www.simple-talk.com/sql/t-sql-programming/consuming-json-strings-in-sql-server/
in your case you need to merge all values from this column to create an array and then apply the workaround functionality mentioned above to create a table. however, I do NOT think this is a solution as it will be very slow. Maybe you could separate those values into separate columns in time of inserting(insert stored proc or backend method, maybe trigger.. not sure about your access rights)
I have quite complicated view in mysql, like
select filter.id as filter_id, person.id, person.name
from person, filter
inner join
...
left join
...
where person_match_filter_condition ...
group by filter.filter_id, person.id, person.name
Query filters person which corresponds domain specific conditions.
Typical use of view is:
select * from where filter_id = some_value
Problem is that mysql cannot optimize query. It applies confition by filter_id AFTER get data for all filters - very ineffective.
Idea to get filter_id from other tables is not good for my case.
How can I transform my query to make it more effective?
Wrap the long query in a procedure, and pass the filters to the procedure call as parameters. Then instead of using views you call the procedure, the procedure will build you the entire query and will run optimized query.
Better yet, you can pass parameters to your views in a simple manner by creating a Function to GET your values from Session Variables. See https://www.stackoverflow.com/questions/14511760 for the technique. This is a copy of my create function you may wish to pattern after.
DELIMITER //
CREATE FUNCTION fn_getcase_id()
RETURNS MEDIUMINT(11)
DETERMINISTIC NO SQL
BEGIN
see stackoverflow.com/questions/14511760 and read ALL the info TWICE or MORE. wh 04/13/2017
RETURN #sv_case_id;
END//
DELIMITER ;
You will need to create a similar FN (one for each variable).