how to convert sql query to hibernate query - sql-server

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.

Related

How do I properly add this query into my existing query within Query Designer?

I currently have the below query written within Query Designer. I asked a question yesterday and it worked on its own but I would like to incorporate it into my existing report.
SELECT Distinct
i.ProductNumber
,i.ProductType
,i.ProductPurchaseDate
,ih.SalesPersonComputerID
,ih.SalesPerson
,ic2.FlaggedComments
FROM [Products] i
LEFT OUTER JOIN
(SELECT Distinct
MIN(c2.Comments) AS FlaggedComments
,c2.SalesKey
FROM [SalesComment] AS c2
WHERE(c2.Comments like 'Flagged*%')
GROUP BY c2.SalesKey) ic2
ON ic2.SalesKey = i.SalesKey
LEFT JOIN [SalesHistory] AS ih
ON ih.SalesKey = i.SalesKey
WHERE
i.SaleDate between #StartDate and #StopDate
AND ih.Status = 'SOLD'
My question yesterday was that I wanted a way to select only the first comment made for each sale. I have a query for selecting the flagged comments but I want both the first row and the flagged comment. They would both be pulling from the same table. This was the query provided and it worked on its own but I cant figure out how to make it work with my existing query.
SELECT a.DateTimeCommented, a.ProductNumber, a.Comments, a.SalesKey
FROM (
SELECT
DateTimeCommented, ProductNumber, Comments, SalesKey,
ROW_NUMBER() OVER(PARTITION BY ProductNumber ORDER BY DateTimeCommented) as RowN
FROM [SalesComment]
) a
WHERE a.RowN = 1
Thank you so much for your assistance.
You can use a combination of row-numbering and aggregation to get both the Flagged% comments, and the first comment.
You may want to change the PARTITION BY clause to suit.
DISTINCT on the outer query is probably spurious, on the inner query it definitely is, as you have GROUP BY anyway. If you are getting multiple rows, don't just throw DISTINCT at it, instead think about your joins and whether you need aggregation.
The second LEFT JOIN logically becomes an INNER JOIN due to the WHERE predicate. Perhaps that predicate should have been in the ON instead?
SELECT
i.ProductNumber
,i.ProductType
,i.ProductPurchaseDate
,ih.SalesPersonComputerID
,ih.SalesPerson
,ic2.FlaggedComments
,ic2.FirstComments
FROM [Products] i
LEFT OUTER JOIN
(SELECT
MIN(CASE WHEN c2.RowN = 1 THEN c2.Comments) AS FirstComments
,c2.SalesKey
,MIN(CASE WHEN c2.Comments like 'Flagged*%' THEN c2.Comments) AS FlaggedComments
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ProductNumber ORDER BY DateTimeCommented) as RowN
FROM [SalesComment]
) AS c2
GROUP BY c2.SalesKey
) ic2 ON ic2.SalesKey = i.SalesKey
JOIN [SalesHistory] AS ih
ON ih.SalesKey = i.SalesKey
WHERE
i.SaleDate between #StartDate and #StopDate
AND ih.Status = 'SOLD'

Convert CROSS APPLY in DB2 database

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.

rank() equivalent for SQL Server 2000

I am using SQL Server 2000 so sadly common table expressions are out but I have the following 2 table structure:
Invoices
id
InvoiceQueryReasons
id
invoice_id
reason
date_queried
I want to inner join from Invoices to InvoiceQueryReasons and only get 1 row for each invoice that selects the most recent InvoiceQueryReasons record.
If I was using a CTE with postgress I would do something like:
SELECT *
FROM
(SELECT
"i"."id", "iq"."reason", "iq"."date_queried",
rank() OVER (PARTITION BY "invoice_id" ORDER BY "date_queried" DESC) AS "invoice_query_rnk",
FROM "invoices" i
INNER JOIN "InvoiceQueryReasons" iq ON ("i"."id" = "iq"."invoice_id")
) AS "iqrs"
INNER JOIN
"invoices" ON ("iqrs"."id" = "invoices"."id")
WHERE
("invoice_query_rnk" = 1)
Apologies if the query is not exactly right, I am writing this on a non-dev machine.
How could I write a similar query in SQL Server 2000 where I do not have common table expression?
This is the way I always used to do this in SQL Server 2000:
FROM Table1
INNER JOIN Table2 ON Table2.PrimaryKey=(
SELECT TOP 1 t2.PrimaryKey
FROM Table2 t2
WHERE t2.ForeignKey=Table1.PrimaryKey
ORDER BY t2 DateColumn DESC
)

SQL Server : error which works well in Oracle

I'm migrating from an Oracle database to SQL Server 2012. Some SQL which works well in Oracle doesn't work with SQL Server.
The following is my SQL and the error.
SELECT
SUM(COUNT(DISTINCT dfc.rentalNumber))
FROM
DueFromClient dfc
WHERE
dfc.facilityId=:facilityId
AND dfc.isRentalComponent = 1
GROUP BY
dfc.rentalNumber
and the error is
Cannot perform an aggregate function on an expression containing an aggregate or a subquery
Remove the SUM from the query, as it has no use, since you are also using GROUP BY. The results will be the same as your original query.
SELECT
COUNT(DISTINCT dfc.rentalNumber)
FROM DueFromClient dfc
WHERE dfc.facilityId = facilityId
AND dfc.isRentalComponent = 1
GROUP BY dfc.rentalNumber
This doesn't make much sense, so I suggest also adding the rentalNumber to the SELECT in order to make sense of your data and to also make full use of the GROUP BY.
SELECT
COUNT(DISTINCT dfc.rentalNumber)
, dfc.rentalNumber
FROM DueFromClient dfc
WHERE dfc.facilityId = facilityId
AND dfc.isRentalComponent = 1
GROUP BY dfc.rentalNumber
You don't need to sum count and group by. Try this:
SELECT
COUNT(DISTINCT dfc.rentalNumber)
FROM
DueFromClient dfc
WHERE
dfc.facilityId=:facilityId
AND dfc.isRentalComponent = 1
sqlfiddle for this query
All of proposed below are suboptimal and returns same result as query above, but they may meet needs of #Sachi Pj with using of same construction as in original query with SUM(COUNT(DISTINCT()) two more options:
SELECT SUM(dfc2.rentalNumber)
FROM
(
SELECT
COUNT(DISTINCT dfc.rentalNumber) rentalNumber
FROM
DueFromClient dfc
WHERE
dfc.facilityId=:facilityId
AND dfc.isRentalComponent = 1
) AS dfc2
GROUP BY dfc2.rentalNumber
sqlfiddle for this query
And without GROUP BY since doubles was eliminated by distinct:
SELECT SUM(dfc2.rentalNumber)
FROM
(
SELECT
COUNT(DISTINCT dfc.rentalNumber) rentalNumber
FROM
DueFromClient dfc
WHERE
dfc.facilityId=:facilityId
AND dfc.isRentalComponent = 1
) AS dfc2
sqlfiddle for this query
You can compare with your original query sqlfiddle to being sure what the results are same.

Filtering Query with NOT EXISTS

I am currently attempting to query a database using SQL Server in Visual Studio. The database in question contains payment information, primarily identifying transactions and their resulting software licenses via OrderID and License ID. Ocassionally, these licenses get revoked due to misuse.
Right now, I'm attempting to run a query that returns all customers based upon this:
Select
[Order].LastName,
[Order].FirstName,
[Order].CompanyOrganization,
[Order].EmailAddress,
[Order].Country,
[License].LicenseID,
[License].InstanceCount
From [Order], [License]
Where
[License].OrderID = [Order].OrderID
AND [Order].Status = 1
AND not exists (Select LicenseID From [LicenseRevocation])
Order by [License].InstanceCount DESC;
The query returns no results, and I know it's because of the "NOT EXISTS" portion. However, I'm not sure why. Can somebody clear up how "EXISTS" works and how to implement it into my query?
The exists() function is true if the query inside it would produce at least one record, thus not exists() is only true if the query inside it would produce zero records.
In your case you are querying the entire LicenseRevocation inside not exists(), so your query would only return anything if that table was completely empty.
You would use a condition in the query to look for a specific record in it, something like this:
not exists (Select * From [LicenseRevocation] Where LicenseID = [License].LicenceID)
That will make the query return the records where there is no corresponding record in the LicenseRevocation table.
You need to define a condition on which you check for values if there exist or not,
Also use ON clause syntax for your joins.
Select [Order].LastName
, [Order].FirstName
, [Order].CompanyOrganization
, [Order].EmailAddress
, [Order].Country
, [License].LicenseID
, [License].InstanceCount
From [Order]
INNER JOIN [License] ON [License].OrderID = [Order].OrderID
Where [Order].[Status] = 1
AND NOT EXISTS (Select 1
From [LicenseRevocation]
WHERE LicenseID = [License].LicenseID) --<-- you are missing this condition
Order by [License].InstanceCount DESC;
this solution uses NOT IN and proper JOIN syntax but should give you the result you want
SELECT [Order].LastName, [Order].FirstName, [Order].CompanyOrganization,
[Order].EmailAddress, [Order].Country,
[License].LicenseID, [License].InstanceCount
FROM [Order]
INNER JOIN [License] ON [License].OrderID = [Order].OrderID
WHERE [Order].Status = 1
AND [License].LicenseID NOT IN (
SELECT LicenseID
FROM [LicenseRevocation] )
ORDER BY [License].InstanceCount DESC;
ps. use table ALIAS
First of all, write your query using proper JOINS, and use a standard LEFT OUTER JOIN:
Select
[Order].LastName,
[Order].FirstName,
[Order].CompanyOrganization,
[Order].EmailAddress,
[Order].Country,
[License].LicenseID,
[License].InstanceCount
From [Order]
INNER JOIN [License] ON [License].OrderID = [Order].OrderID
LEFT OUTER JOIN [LicenseRevocation] ON [License].LicenseID = [LicenseRevocation].LicenseID
Where [Order].Status = 1
AND [LicenseRevocation].LicenseID IS NULL
Order by [License].InstanceCount DESC;

Resources