We have a table:
q)t:([] id:("ab";"cd";"ef";"gh";"ij"); refid:("";"ab";"";"ef";""); typ:`BUY`SELL`BUY`SELL`BUY)
q)t
id refid typ
---------------
"ab" "" BUY
"cd" "ab" SELL
"ef" "" BUY
"gh" "ef" SELL
"ij" "" BUY
Now our requirement is to add a column named 'event' to the table which is marked as 'N' if id of BUY type matches with refid of SELL type and refid is not null else mark event as 'Y'.
I have written below query which works perfectly fine but has a scope of optimization.
Desired Output:
id refid typ event
---------------------
"ab" "" BUY N
"cd" "ab" SELL N
"ef" "" BUY N
"gh" "ef" SELL N
"ij" "" BUY Y
Query used:
q)update event:`N from (update event:?[([]id) in (select id:refid from t where typ=`SELL, not refid like "");`N;`Y] from t) where typ=`SELL, not refid like ""
Please help me optimize above query.
You could try something like this which works for the date you have provided
q)update eve:?[(typ=`BUY) &(not any(`$id)=/:`$refid);`Y;`N] from t
id refid typ eve
-------------------
"ab" "" BUY N
"cd" "ab" SELL N
"ef" "" BUY N
"gh" "ef" SELL N
"ij" "" BUY Y
Related
I have this table called people
id
name
lastname
1
John
Smith
2
Robert
Williams
3
Peter
Walker
if I run the query
select CASE WHEN id is null THEN '0' ELSE id END as id
from people
where id='2'
The result is:
| id
| ----
| 2
I want to display id as 0 when it is Null in the table, but when I run
select CASE WHEN id is null THEN '0' ELSE id END as id
from people
where id='4'
id
My expected result is:
| id
| ----
| 0
A top-level SQL query that doesn't match any rows will return zero rows, which is different from returning NULL. This makes sense, because it lets you distinguish between the result for SELECT email FROM users WHERE id=4 when there is no such user and the result when there is a user but their email is null.
However, a subquery that returns no rows will evaluate to NULL the way you expected. So you can rewrite your code like this:
SELECT COALESCE( (SELECT id FROM people WHERE id = '4'), 0 );
COALESCE(x,y) is shorthand for CASE WHEN x IS NULL THEN y ELSE x END. It's helpful in cases like this where the expression for x is long and you don't want to have to write it twice.
Given the following table, I need to merge the fields in different "id" only if they are the same type (person or dog), and always as the value of every field of an "id" is contained in the values of other "ids".
id
being
feature
values
1
person
name
John;Paul
1
person
surname
Smith
2
dog
name
Ringo
3
dog
name
Snowy
4
person
name
John
4
person
surname
5
person
name
John;Ringo
5
person
surname
Smith
In this example, the merge results should be as follows:
1 and 4 (Since 4's name is present in 1's name and 4's surname is empty)
1 and 5 cannot be merged (the name field show different values)
4 and 5 can be merged
2 and 3 (dogs) cannot be merged. They have only the field "name" and they do not share values.
2 and 3 cannot be merged with 1, 4, 5 since they have different values in "being".
id
being
feature
values
1
person
name
John;Paul
1
person
surname
Smith
2
dog
name
Ringo
3
dog
name
Snowy
5
person
name
John;Ringo
5
person
surname
Smith
I have tried this:
UPDATE table a
SET values = (SELECT array_to_string(array_agg(distinct values),';') AS values FROM table b
WHERE a.being= b.being
AND a.feature= b.feature
AND a.id<> b.id
AND a.values LIKE '%'||a.values||'%'
)
WHERE (select count (*) FROM (SELECT DISTINCT c.being, c.id from table c where a.being=c.being) as temp) >1
;
This doesn't work well because it will merge, for example, 1 and 5. Besides, it duplicates values when merging that field.
One option is to aggregate names with surnames on "id" and "being". Once you get a single string per "id", a self join may find when a full name is completely included inside another (where the "being" is same for both "id"s), then you just select the smallest fullname, candidate for deletion:
WITH cte AS (
SELECT id,
being,
STRING_AGG(values, ';') AS fullname
FROM tab
GROUP BY id,
being
)
DELETE FROM tab
WHERE id IN (SELECT t2.id
FROM cte t1
INNER JOIN cte t2
ON t1.being = t2.being
AND t1.id > t2.id
AND t1.fullname LIKE CONCAT('%',t2.fullname,'%'));
Check the demo here.
I have a table 't' and a sub-table 'st'.
If the values of subtable matches with that of table then we want to update the 't' table with value Y in new column named 'exists' else N.
Table:
q)t:([] id:("ab";"cd";"ef";"gh";"ij"); refid:("";"ab";"";"ef";""); typ:`BUY`SELL`BUY`SELL`BUY)
q)t
id refid typ
---------------
"ab" "" BUY
"cd" "ab" SELL
"ef" "" BUY
"gh" "ef" SELL
"ij" "" BUY
subtable:
q)st:([] id:("ab";"cd"); typ:`BUY`SELL)
q)st
id typ
---------
"ab" BUY
"cd" SELL
Desired Output:
id refid typ exists
----------------------
"ab" "" BUY Y
"cd" "ab" SELL Y
"ef" "" BUY N
"gh" "ef" SELL N
"ij" "" BUY N
Tried various things like any,each,right each, in - but could get desired result.
One option is to use left join (lj)
q) update `N^exists from t lj `id`typ xkey update exists:`Y from st
id refid typ exists
----------------------
"ab" "" BUY Y
"cd" "ab" SELL Y
"ef" "" BUY N
"gh" "ef" SELL N
"ij" "" BUY N
Here I have defined id and typ columns as keys for st table. Change the keys of table st to the columns you want to match with table t.
Rahul's answer is by far neater than what follows, but the following may be more performant, depending on your table sizes. By matching the columns in t to st (indexing and flipping) and determining a boolean of matching rows, passed into YN boolean dictionary we can produce the desired result.
q)`N`Y (flip cols[st]!t[cols[st]]) in st
`Y`Y`N`N`N
q)update exists:(`N`Y (flip cols[st]!t[cols[st]]) in st) from t
id typ refid exists
----------------------
"ab" BUY "" Y
"cd" SELL "ab" Y
"ef" BUY "" N
"gh" SELL "ef" N
"ij" BUY "" N
q)\t:100000 update exists:(`N`Y (flip cols[st]!t[cols[st]]) in st) from t
446
q)\t:100000 update `N^exists from t lj `id`typ xkey update exists:`Y from st
856
Rahuls lj method may generalize to be faster once attributes are applied.
Edited as per Rahuls observations,
Another alternative to the other answers is to extract the sub table columns from t (cols[st]#t)and check they are in st:
update exists:(cols[st]#t)in st from t
id refid typ exists
----------------------
"ab" "" BUY 1
"cd" "ab" SELL 1
"ef" "" BUY 0
"gh" "ef" SELL 0
"ij" "" BUY 0
If you need to display the result as YN then you can make a slight modification to get the following:
update exists:`N`Y(cols[st]#t)in st from t
id refid typ exists
----------------------
"ab" "" BUY Y
"cd" "ab" SELL Y
"ef" "" BUY N
"gh" "ef" SELL N
"ij" "" BUY N
I have a scenario where I am joining three tables and getting the results.
My problem is i have apply limit for joined table.
Take below example, i have three tables 1) books and 2) Customer 3)author. I need to find list of books sold today with author and customer name however i just need last nth customers not all by passing books Id
Books Customer Authors
--------------- ---------------------- -------------
Id Name AID Id BID Name Date AID Name
1 1 1 ABC 1 A1
2 2 1 CED 2 A2
3 3 2 DFG
How we can achieve this?
You are looking for LATERAL.
Sample:
SELECT B.Id, C.Name
FROM Books B,
LATERAL (SELECT * FROM Customer WHERE B.ID=C.BID ORDER BY ID DESC LIMIT N) C
WHERE B.ID = ANY(ids)
AND Date=Current_date
Based on response to my question here
I have simple table:
id, name, city
and I created a class for it:
#Entity(tableName = "table2")
data class Table2 #JvmOverloads constructor(
#PrimaryKey #ColumnInfo(name = "id") val id: Int? = 0,
#ColumnInfo(name = "name") val name: String? = "",
#ColumnInfo(name = "city") val city: String? = ""
)
I entered next rows:
1 John London
2 Mary Paris
3 John Paris
4 Samy London
I want to get such a result:
London Paris
John 1 1
Mary 0 1
Samy 1 0
Total 2 2
It's not important to get Total row together with John, Mary and Samy lines, now I just want to get a simple response without complex query.
Since I want to get response list of classes containing 3 fields:
city, Int, Int
I created a class for it:
data class CityName constructor(
val name: String? = "",
val countLondon: Int? = 0,
val countParis: Int? = 0
)
Next I tried to create a SQLite query to get a result from Android Room.
#Query("SELECT name,
COUNT(CASE WHEN city = :london THEN 1 ELSE 0 END) 'London',
COUNT(CASE WHEN city = :paris THEN 1 ELSE 0 END) 'Paris'
FROM table2 GROUP BY name")
fun getPivot_CityNameList(london: String, paris: String): List<CityName>
I excluded/included from the query 'London' and 'Paris', I swapped some words etc.
but I always get a result like this:
John, countLondon: null, countParis: null
Mary, countLondon: null, countParis: null
Samy, countLondon: null, countParis: null
It's easy to get subset from a table, like city and id or to get only a count of some field but
I have no idea how to combine subset (name) with two counts in one response.
What is wrong: my query or CityName class response?
How to fix it?
ANSWER
The correct query is
SELECT name,
COUNT(CASE WHEN city = :london THEN 1 END) as countLondon,
COUNT(CASE WHEN city = :paris THEN 1 END) as countParis
FROM table2 GROUP BY name
as mentioned in the accepted answer I missed as countLondon
but also I deleted ELSE 0 from CASE statement
Your CityName fields' names should match your query's columns' aliases.
That's how Room then copies values from SQLite cursor to fields of this class.
So change aliases for columns, and your code should work:
#Query("SELECT name,
COUNT(CASE WHEN city = :london THEN 1 ELSE 0 END) as countLondon, // <- change here
COUNT(CASE WHEN city = :paris THEN 1 ELSE 0 END) as countParis // <- change here
FROM table2 GROUP BY name")
fun getPivot_CityNameList(london: String, paris: String): List<CityName>