Convert CROSS APPLY in DB2 database - sql-server

I am trying to convert the below MSSQL query into DB2 query.
But i am facing issues . I got to know "CROSS APPLY" doesnt exist for DB2
SQL Server query:
SELECT DISTINCT p.ID,
p.COMPANY,
p.NAME,
format(d.startTime, 'yyyy-MM-dd HH:mm:ss.fff')
FROM PROCESS p
CROSS APPLY (SELECT MAX(END_TIME) AS startTime FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS)) AS d
WHERE p.ID = (SELECT MAX(ID) FROM PROCESS)
Error:
Error: com.ibm.db2.jcc.c.SqlException: DB2 SQL error: SQLCODE: -104, SQLSTATE: 42601, SQLERRMC: APPLY;N process
CROSS;JOIN
How the above query can be converted into DB2 query format?

The SQL Server manual says that a CROSS APPLY is used with table functions.
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql?view=sql-server-ver15#using-apply
That the right_table_source can use a table-valued function that takes a column from the left_table_source as one of the arguments of the function.
Your example does not use any, so I assume it is simply the equivalent to a CROSS JOIN in Db2.
By the way, this statement would likely get the same result (assuming COMPANY and NAME are the same for a given ID)
SELECT
ID
, COMPANY
, NAME
, format(END_TIME, 'yyyy-MM-dd HH:mm:ss.fff')
FROM
( SELECT *
, ROW_NUMBER() OVER(ORDER BY ID DESC, END_TIME DESC) AS RN
FROM
PROCESS p
)
WHERE
RN = 1
This might or might not be more optimal at execution time

The DB2 equivalent is the terribly named TABLE operator. The name makes it very challenging to find documentation.
See if this works for your query.
SELECT DISTINCT p.ID,
p.COMPANY,
p.NAME
FROM PROCESS p
JOIN TABLE (
SELECT ID
,MAX(END_TIME) AS startTime
FROM PROCESS
WHERE ID = (
SELECT MAX(ID)
FROM PROCESS
)
GROUP BY ID
) AS D
ON P.ID = D.ID
However, I've found CTEs in DB2 to be very effective. In SQL Server, a CTE is almost like a layer applied to a query, similar to a view. The expressions in the CTE are generally combined with the underlying statements and executed as a single statement.
I'm not a DB2 expert, but it seems to me that a CTE is materialized to an internal table and the result is combined with the remaining statements.
With the same query, data and indexes, DB2 and SQL Server can have very different performance when CTEs are involved.

Related

how to convert sql query to hibernate query

i have q sql query where i am fetching data using subquery and left join. it is working properly on sql server but when i use this query in hibernate using HQL it says unexpected token (;
below is my query which is working fine on sql server -
SELECT IH.vendorName, IH.C, IHP.CP FROM (SELECT vendorName, count(*) as C
FROM InvoiceHeader GROUP BY vendorName) IH LEFT JOIN (SELECT vendorName,
count(*) AS CP FROM InvoiceHeader WHERE invoiceStatus='Processed' GROUP BY
vendorName) IHP ON IHP.vendorName=IH.vendorName ORDER BY IH.C DESC
here i am trying to convert my sql query to HQL
Query q = sessionFactory.getCurrentSession().createQuery("SELECT
IH.vendorName, IH.C, IHP.CP FROM (SELECT vendorName, count(*) as C FROM
InvoiceHeader GROUP BY vendorName) IH LEFT JOIN (SELECT vendorName, count(*)
AS CP FROM InvoiceHeader WHERE invoiceStatus='Processed' GROUP BY
vendorName) IHP ON IHP.vendorName=IH.vendorName ORDER BY IH.C DESC");
but i am getting this error at
org.hibernate.hql.internal.ast.ErrorCounter reportError
ERROR: line 1:41: unexpected token: (
org.hibernate.hql.internal.ast.ErrorCounter reportError
ERROR: line 1:61: unexpected token: count
Here's partially how to do it.
First, your query needs to be simplified, because the joins are not at all efficient. MSSQL does not support FILTER, but Modern SQL shows us a sufficent alternative.
SELECT vendorName, sum(case when invoiceStatus = 'Processed' then 1 end) as CP, count(*) as C FROM InvoiceHeader GROUP BY vendorName ORDER BY C DESC;
Secondly you could use createNativeQuery if it turns out to be impossible to translate it to HQL, but this is my attempt to do so:
SELECT I.vendorName, COUNT(I) as C, SUM(CASE WHEN I.invoiceStatus = 'Processed' THEN 1 END) AS CP FROM InvoiceHeader I GROUP BY I.vendorName ORDER BY C DESC
It is important you use the table alias, so that it will resolve properly within the query, even though it's the only table.

Why would one specific SQL Server Database fail to convert VARCHAR to NUMERIC?

Is it possible that 2 different SQL Server databases, having the same schema and data, could process a query in a different way, and one of them throwing a " Error converting data type varchar to numeric. " error, while the other one doesn't?
For example, see this query:
SELECT count(*) FROM DEPARTMENTS dpt
inner join
EMPLOYEES emp
on dpt.subgroup_id = emp.subgroup_id
WHERE
emp.employee_id = 12856
and
dpt.department_id in
(
select cm.mapped_value from CONFIGURATIONS c
inner join
CONFIG_MAPPINGS cm on cm.configuration_id = c.id
where
c.name = 'Department that can override self-destruction protocol'
)
The inner select subquery is returning either a value that can be cast to a number, or no results at all. So what I've noticed is that:
One database running Microsoft SQL Server 2012 - 11.0.2100.60 executes the query without errors, as long as the inner statement will not return a non-numeric value
Another database running Microsoft SQL Server 2012 (SP4-GDR) (KB4057116) - 11.0.7462.6 is always throwing the "Error converting data type varchar to numeric." error, regardless of what the inner select query returns.
I've also noticed that if I change the subquery with another one that returns exactly the same, but without the inner join, the 2nd database is not throwing the error
SELECT count(*) FROM DEPARTMENTS dpt
inner join
EMPLOYEES emp
on dpt.subgroup_id = emp.subgroup_id
WHERE
emp.employee_id = 12856
and
dpt.department_id in
(
select cm.mapped_value FROM CONFIG_MAPPINGS
WHERE cm.configuration_id = 'DPT_OVERRIDE_SDP'
)
I know that I should improve the query anyway, adding a ISNUMERIC check, but just out of curiosity, my question is:
Could it be that one these databases is processing the query in a different way (perhaps different execution plan) and always exposes the error, while the other database manages to execute the query?
Execute this queries:
select cm.mapped_value FROM CONFIG_MAPPINGS
WHERE cm.configuration_id = 'DPT_OVERRIDE_SDP' AND ISNUMERIC(cm.mapped_value)<>1
and
select cm.mapped_value from CONFIGURATIONS c
inner join
CONFIG_MAPPINGS cm on cm.configuration_id = c.id
where
c.name = 'Department that can override self-destruction protocol'
AND ISNUMERIC(cm.mapped_value)<>1
to see which values are the reason of error. Then you can use "ISNUMERIC(cm.mapped_value)=1" in your internal query to ignore them.
After comparing the execution plans on both databases, I've noticed they are quite different.
If the plan was joining DEPARTMENTS and CONFIG_MAPPINGS before joining CONFIG_MAPPINGS with CONFIGURATIONS, and before the c.name = .. filter was applied, then we would be joining on dpt.department_id (numeric) = cm.mapped_value, while mapped_value would also contain non-numeric values at that point.
This is the case with the 2nd database, and this explains the conversion error

SQL FROM with INTO #table not working

I'm dusting off my SQL (or in this case TSQL) and have been writing a stored procedure. Up to this point in my code I can get fine execution with LINQPad as I test my code on the side. However, this particular block, where I have the top scores for each user added to temporary table #QuizScores is delivering a generic syntax error
Incorrect syntax near the keyword 'SELECT'
I've tried to move all the pieces of this statement around but seem to be failing. Any insight you might be able to share?
Here's the statement:
SELECT *
INTO #QuizScores
FROM (SELECT ROW_NUMBER()
OVER (
PARTITION BY UserID
ORDER BY count(#QuizResponses.IsCorrectAnswer) DESC) AS
RowNumber,
UserID,
count(#QuizResponses.IsCorrectAnswer) AS
Points
FROM #QuizResponses)
JOIN #QuizResponses
ON #QuizResponses.QuizAttemptID = #QuizAttemptList.QuizAttemptID;
i have taken some assumed data inserted into temp table and modified syntax according to your requirement
IF OBJECT_ID('tempdb..#QuizResponses') IS NOT NULL
DROP TABLE #QuizResponses
GO
IF OBJECT_ID('tempdb..#QuizScores') IS NOT NULL
DROP TABLE #QuizScores
CREATE TABLE #QuizResponses (UserId int,correctanswer int,IsCorrectAnswer varchar(1),QuizAttemptID INT)
INSERT INTO #QuizResponses (UserId,correctanswer,IsCorrectAnswer)values (1,1,'Y',2),(2,1,'N',2)
select P.RowNumber,P.Points,P.UserId INTO #QuizScores from (
SELECT ROW_NUMBER() OVER (PARTITION BY a.UserID
ORDER BY count(a.IsCorrectAnswer) DESC) AS RowNumber, a.UserID ,
count(a.IsCorrectAnswer) AS Points
FROM #QuizResponses a
JOIN #QuizResponses S ON S.UserId = A.UserId
group by a.UserId )P
[EDIT: This reflects an earlier revision of the question.]
From MSDN:
SELECT…INTO creates a new table in the default filegroup
Emphasis added.

Group by working in SQL Server 2000 but not in SQL Server 2005

I used following query in SQL Server 2000
SELECT
U.FirstName,
SUM(VE.Score)AS Score, SUM(VE.QuizTime) AS Time,
SUM(VE.IsQuizType) AS QuizesAttempted,
SUM(VE.IsProgrammingType) AS ProgrammingProblemsAttempted
from
Users U INNER JOIN VirtualExercise VE on U.UserID=VE.UserID
where U.UserID IN( 10,11 ) AND ProgramID = 2
group by U.FirstName
order by VE.Score desc
It working fine in SQL Server 2000 but not working in SQL Server 2005.
Gives following error:
Column "VirtualExercise.Score" is
invalid in the ORDER BY clause because
it is not contained in either an
aggregate function or the GROUP BY
clause. --- Inner Exception
Please help...
SQL Server 2005 is correct: you are trying to order by a value that doesn't exist in the output because you must either GROUP on it or aggregate it. VE.Score is neither. SQL Server 2000 allowed some ambiguities like this (edit: see "ORDER BY clause" at http://msdn.microsoft.com/en-us/library/ms143359(SQL.90).aspx for info on this)
I assume you mean
ORDER BY SUM(VE.Score) DESC
SQL Server 2000 is kind of dumb where it comes to table and column aliases in the result set. Provided an explicit alias column is introduced (e.g. Score in your case, Col3 in the example below), it doesn't matter what table alias you use in the ORDER BY clause:
create table #T1 (
ID int not null primary key,
Col1 varchar(10) not null
)
go
create table #T2 (
ID int not null primary key,
Col2 varchar(10) not null
)
go
insert into #T1 (ID,Col1)
select 1,'abc' union all
select 2,'def'
go
insert into #T2 (ID,Col2)
select 1,'zyx' union all
select 2,'wvu'
go
select *,1 as Col3 from #T1 t1 inner join #T2 t2 on t1.ID = t2.ID order by t2.Col3
Since you presumably want to sort by the computed Score column, just remove the VE. prefix.
Since you have defined SUM(VE.Score)AS Score in select , do you mean ORDER BY Score DESC

How to create RowNum column in SQL Server?

In Oracle we have "rownum".
What can I do in SQL Server?
In SQL Server 2005 (and 2008) you can use the ROW_NUMBER function, coupled with the OVER clause to determine the order in which the rows should be counted.
Update
Hmm. I don't actually know what the Oracle version does. If it's giving you a unique number per row (across the entire table), then I'm not sure there's a way to do that in SQL Server. SQL Server's ROW_NUMBER() only works for the rows returned in the current query.
If you have an id column, you can do this:
select a.*,
(select count(*) from mytable b where b.id <= a.id) as rownum
from mytable a
order by id;
Of course, this only works where you're able to order rownums in the same (or opposite) order as the order of the ids.
If you're selecting a proper subset of rows, of course you need to apply the same predicate to the whole select and to the subquery:
select a.*,
(select count(*) from table b where b.id <= a.id and b.foo = 'X') as rownum
from table a where a.foo = 'X'
order by id;
Obviously, this is not particularly efficient.
Based on my understanding, you'd need to use ranking functions and/or the TOP clause. The SQL Server features are specific, the Oracle one combines the 2 concepts.
The ranking function is simple: here is why you'd use TOP.
Note: you can't WHERE on ROWNUMBER directly...
'Orable:
select
column_1, column_2
from
table_1, table_2
where
field_3 = 'some value'
and rownum < 5
--MSSQL:
select top 4
column_1, column_2
from
table_1, table_2
where
field_3 = 'some value'

Resources