How to express window function in Slick? - scala

I am new to Slick and I am having hard time to express SQL window function in Slick. My database profile is SQL Server so I cannot use slick-pg.
The problem that I want to solve is simple in SQL (below). Essentially, it is just to find the largest rowid for each destination. How to write in this in Slick?
select rowid, destination
from (
select rowid
, destination
, row_number() over (partition by destination order by rowid desc) as rowOrder
from DestinationTable
)
where rowOrder = 1

Check user defined functions in slick,
https://scala-slick.org/doc/3.1.0/userdefined.html
Here is an example, I achieved same requirement using SimpleLiteral, ideally SimpleFunction might be good choice, but SimpleLiteral works for my use case.
and used a subquery to filter over rowNum data.
val rowNumCol = SimpleLiteral[Int]("ROW_NUMBER() OVER(PARTITION BY replaceGroupByColumnName order by sortColumnName desc)")
val innerQueryRowNum = queryFilter.map(jb => (jb.guid, rowNumCol))
val resultQueryFilter = TableQuery.join(innerQueryRowNum).on((tb, tbs) => tb.guid === tbs._1).filter(tb => tb._2._2 === 1).map(tb => tb._1)

Slick does not support this functionality in its query DSL, so you'll need to use Slick's support for plain SQL queries.
http://scala-slick.org/doc/3.2.3/sql.html

Related

Can postgreSQL OnConflict combine with JSON obejcts?

I wanted to perform a conditional insert in PostgreSQL. Something like:
INSERT INTO {TABLE_NAME} (user_id, data) values ('{user_id}', '{data}')
WHERE not exists(select 1 from files where user_id='{user_id}' and data->'userType'='Type1')
Unfortunately, insert and where does not cooperate in PostGreSQL. What could be a suitable syntax for my query? I was considering ON CONFLICT, but couldn't find the syntax for using it with JSON object. (Data in the example)
Is it possible?
Rewrite the VALUES part to a SELECT, then you can use a WHERE condition:
INSERT INTO { TABLE_NAME } ( user_id, data )
SELECT
user_id,
data
FROM
( VALUES ( '{user_id}', '{data}' ) ) sub ( user_id, data )
WHERE
NOT EXISTS (
SELECT 1
FROM files
WHERE user_id = '{user_id}'
AND data -> 'userType' = 'Type1'
);
But, there is NO guarantee that the WHERE condition works! Another transaction that has not been committed yet, is invisible to this query. This could lead to data quality issues.
You can use INSERT ... SELECT ... WHERE ....
INSERT INTO elbat
(user_id,
data)
SELECT 'abc',
'xyz'
WHERE NOT EXISTS (SELECT *
FROM files
WHERE user_id = 'abc'
AND data->>'userType' = 'Type1')
And it looks like you're creating the query in a host language. Don't use string concatenation or interpolation for getting the values in it. That's error prone and makes your application vulnerable to SQL injection attacks. Look up how to use parameterized queries in your host language. Very likely for the table name parameters cannot be used. You need some other method of either whitelisting the names or properly quoting them.

Slick:Insert into a Table from Raw SQL Select

Insert into a Table from Raw SQL Select
val rawSql: DBIO[Vector[(String, String)]] = sql"SELECT id, name FROM SomeTable".as[(String, String)]
val myTable :TableQuery[MyClass] // with columns id (String), name(String) and some other columns
Is there a way to use forceInsert functions to insert data from select into the tables?
If not, Is there a way to generate a sql string by using forceInsertStatements?
Something like:
db.run {
myTable.map{ t => (t.id, t.name)}.forceInsert????(rawSql)
}
P.S. I don't want to make two I/O calls because my RAW SQL might be returning thousands of records.
Thanks for the help.
If you can represent your rawSql query as a Slick query instead...
val query = someTable.map(row => (row.id, row.name))
...for example, then forceInsertQuery will do what you need. An example might be:
val action =
myTable.map(row => (row.someId, row.someName))
.forceInsertQuery(
someTable.map(query)
)
However, I presume you're using raw SQL for a good reason. In that case, I don't believe you can use forceInsert (without a round-trip to the database) because the raw SQL is already an action (not a query).
But, as you're using raw SQL, why not do the whole thing in raw SQL? Something like:
val rawEverything =
sqlu" insert into mytable (someId, someName) select id, name from sometable "
...or similar.

how to call spring jap query none parameters

I use spring data jpa with native query
I have already some query like this
How to use native query none parameter.
String q="SELECT t1.blockNumber-1 FROM someTAble t1 LEFT JOIN someTAble t2 ON t2.blockNumber = t1.blockNumber-1 WHERE t2.blockNumber IS NULL AND t1.blockNumber> 0 ORDER BY t1.blockNumber";
#Query(value = q,nativeQuery = true)
List<Entity> findByBlockNumberIs();
they are occur errors Column 'sequence' not found.
That query means are when i insert some Contiguous data int value then i find missing data.
But
this query working
SELECT *,t1.blockNumber-1 FROM someTAble t1 LEFT JOIN someTAble t2 ON t2.blockNumber = t1.blockNumber-1 WHERE t2.blockNumber IS NULL AND t1.blockNumber> 0 ORDER BY t1.blockNumber
The difference between the two queries is whether there is a '*' or not
how to change simple to my query.
How to i changed error
OR How to use spring data jpa predicate
QEntity qBe1= QEntity .blockEntity;
QEntity qBe2= QEntity .blockEntity;
build.and(qBe2.blockNumber.eq(be.getBlockNumber()-1))
.and(qBe2.blockNumber.isNull().and(qBe1.blockNumber.gt(0)));
is predicate can use left join?
well...
use this.
List<Integer> findByBlockNumber()

Sub Query in select clause with Squeryl

I'm trying to replicate the following query usine Squeryl.
SELECT c.order_number,p.customer,p.base,(
SELECT sum(quantity) FROM "Stock" s where s.base = p.base
) as stock
FROM "Card" c, "Part" p WHERE c."partId" = p."idField";
I have the following code for selecting the Cards and Parts but I cannot see a way to add a sumation into the select clause.
from(cards, parts)((c,p) =>
where(c.partId === p.id)
select(c,p)
Any help is much appreciated!
In Squeryl, you can use any Queryable object in the from clause of your query. So, to create a subquery, something like the following should work for you:
def subQuery = from(stock)(s => groupBy(s.base) compute(sum(s.quantity)))
from(cards, parts, subquery)((c, p, sq) =>
where(c.partId === p.idField and sq.key === p.base)
select(c.orderNumber, p.customer, sq.measures))
Of course the field names may vary slightly, just guessing at the class definitions. If you want the whole object for cards and parts instead of the single fields from the original query - just change the select clause to: select(c, p, sq.measures)

Paging in Entity Framework

In Entity Framework, using LINQ to Entities, database paging is usually done in following manner:
int totalRecords = EntityContext.Context.UserSet.Count;
var list = EntityContext.Context.UserSet
.Skip(startingRecordNumber)
.Take(pageSize)
.ToList();
This results in TWO database calls.
Please tell, how to reduce it to ONE database call.
Thank You.
Whats wrong with two calls? They are small and quick queries. Databases are designed to support lots of small queries.
A developing a complex solution to do one query for paging isn't going give you much pay off.
Using Esql and mapping a stored procedure to an entity can solve the problem.
SP will return totalRows as output parameter and current page as resultset.
CREATE PROCEDURE getPagedList(
#PageNumber int,
#PageSize int,
#totalRecordCount int OUTPUT
AS
//Return paged records
Please advise.
Thank You.
Hmmm... the actual call that uses paging is the second one - that's a single call.
The second call is to determine the total number of rows - that's quite a different operation, and I am not aware of any way you could combine those two distinct operations into a single database call with the Entity Framework.
Question is: do you really need the total number of rows? What for? Is that worth a second database call or not?
Another option you would have is to use the EntityObjectSource (in ASP.NET) and then bind this to e.g. a GridView, and enable AllowPaging and AllowSorting etc. on the GridView, and let the ASP.NET runtime handle all the nitty-gritty work of retrieving the appropriate data page and displaying it.
Marc
ALTER proc [dbo].[GetNames]
#lastRow bigint,
#pageSize bigint,
#totalRowCount bigint output
as
begin
select #totalRowCount = count(*) from _firstNames, _lastNames
select
FirstName,
LastName,
RowNumber
from
(
select
fn.[FirstName] as FirstName,
ln.[Name] as LastName,
row_number() over( order by FirstName ) as RowNumber
from
_firstNames fn, _lastNames ln
) as data
where
RowNumber between ( #lastRow + 1 ) and ( #lastRow + #pageSize )
end
There is no way to get this into one call, but this works fast enough.
This queries are too small for DBManager and I can not understand why you want to do this, anyway for reduce it to ONE database call use this:
var list = EntityContext.Context.UserSet
.Skip(startingRecordNumber)
.Take(pageSize)
.ToList();
int totalRecords = list.Count;
Suppose you want to get the details of Page 2 with a pagesize=4
int page =2;
int pagesize=4;
var pagedDetails= Categories.Skip(pagesize*(page-1)).Take(pagesize)
.Join(Categories.Select(item=>new {item.CategoryID,Total = Categories.Count()}),x=>x.CategoryID,y=>y.CategoryID,(x,y)=>new {Category = x,TotalRows=y.Total});
The Output will have all details of Category and TotalRows.
One DB call.
Generated SQL
-- Region Parameters
DECLARE #p0 Int = 2
DECLARE #p1 Int = 4
-- EndRegion
SELECT [t2].[CategoryID], [t2].[CategoryName], [t2].[Description], [t2].[Picture], [t5].[value] AS [TotalRows]
FROM (
SELECT [t1].[CategoryID], [t1].[CategoryName], [t1].[Description], [t1].[Picture], [t1].[ROW_NUMBER]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[CategoryID], [t0].[CategoryName]) AS [ROW_NUMBER], [t0].[CategoryID], [t0].[CategoryName], [t0].[Description], [t0].[Picture]
FROM [Categories] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p0 + #p1
) AS [t2]
INNER JOIN (
SELECT [t3].[CategoryID], (
SELECT COUNT(*)
FROM [Categories] AS [t4]
) AS [value]
FROM [Categories] AS [t3]
) AS [t5] ON [t2].[CategoryID] = [t5].[CategoryID]
ORDER BY [t2].[ROW_NUMBER]