Translate SQLite query, with subquery, into Peewee statement - sqlite

I've got a SQL statement that does what I need, but I'm having trouble converting it into the correlated Peewee statement. Here's the SQL I have now, note that I'm using a subquery right now, but I don't care that it's a subquery either way.
select t.name,
count(a.type_id) as total,
(
select count(id)
from assignment a
where a.course_id = 7
and a.due_date < date()
and a.type_id = t.id
group by a.type_id
order by a.type_id
) as completed
from assignment a
inner join type t on t.id = a.type_id
where a.course_id = 7
group by a.type_id
order by a.type_id
Here's the closest I've come to the Peewee statement. Currently I'm aliasing a static number in the query just so that I have a value to work with in my template, so please ignore that part.
Assignment.select(
Type.name,
fn.Lower('1').alias('completed'),
fn.Count(Type.id).alias('total'),
).naive().join(Type).where(
Assignment.course==self,
).group_by(Type.id).order_by(Type.id)

Have you tried just including the subquery as part of the select?
Something like this?
query = (Assignment
.select(
Type.name,
fn.COUNT(Type.id).alias('total'),
Assignment.select(fn.COUNT(Assignment.id)).where(
(Assignment.due_date < fn.DATE()) &
(Assignment.course == 7) &
(Assignment.type == Type.id)
).group_by(Assignment.type).alias('completed'))
.join(Type)
.where(Assignment.course == 7)
.group_by(Type.name))

Related

what is different between using Variable and not in Mariadb?

I just want to get some hint. Is there a type issue?
issue case.
SET #ids = '4094,8562,11144,3017,5815,11121,1957,4095,8563,11145,3018,5816,8527,11122,1959,4096,8564,3020,5817,8528,11123,1961,4097,8571,3021,6020,8535,11128,1962,5181,8572,3581,6021';
this #ids value is actually collected by GROUP_CONCAT() from the subquery;
SELECT
ifnull(sum(case when a.student IS NOT NULL then total END), 0)
from
tb_class a
WHERE
a.id IN (#ids)
and a.date >= '2023-02-01' AND a.DATE <= '2023-02-02'
==> 0
correct case2.
SELECT
ifnull(sum(case when a.student IS NOT NULL then total END), 0)
from
tb_class a
WHERE
a.id IN (4094,8562,11144,3017,5815,11121,1957,4095,8563,11145,3018,5816,8527,11122,1959,4096,8564,3020,5817,8528,11123,1961,4097,8571,3021,6020,8535,11128,1962,5181,8572,3581,6021)
and a.date >= '2023-02-01' AND a.DATE <= '2023-02-02'
==> 54
I got answer from googling. use function FIND_IN_SET()
SELECT
ifnull(sum(case when a.student IS NOT NULL then total END), 0)
from
tb_class a
WHERE
FIND_IN_SET(a.id, #ids)
and a.date >= '2023-02-01' AND a.DATE <= '2023-02-02'
Variables store single values, not lists. Your #ids is just a string that happens to have a comma separated list of numbers. The IN operator only compares against an explicit list; what you are doing is no different than a.id = #ids (which will actually be true, with a warning, for the first number in the list if id is a numeric type, since the string will be converted to a number and the trailing non-numeric portion discarded).
Sometimes you do want to work with a string containing a list of ids such as this, for instance if you have a query that reads many rows that you want to use to produce a small list of ids to update, without the update locking those all the rows read. Then you can use dynamic sql:
SET #ids = '4094,8562,...';
SET #sql = concat('select * from a where a.id in (',#ids,')');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
Or, in mariadb since 10.2,
EXECUTE IMMEDIATE concat('select * from a where a.id in (',#ids,')');
Another alternative is to use FIND_IN_SET, as shown in another answer, but that will not use an index to look up ids, so may be inefficient.

Query fails to execute after converting a column from Varchar2 to CLOB

I have a oracle query
select id from (
select ID, ROW_NUMBER() over (partition by LATEST_RECEIPT order by ID) rownumber
from Table
where LATEST_RECEIPT in
(
select LATEST_RECEIPT from Table
group by LATEST_RECEIPT
having COUNT(1) > 1
)
) t
where rownumber <> 1;
The data type of LATEST_RECEIPT was earlier varchar2(4000) and this query worked fine. Since the length of the column needs to be extended i modified it to CLOB, after which this fails. Could anyone help me fix this issue or provide a work around?
You can change your inner query to look for other rows with the same last_receipt value but a different ID (assuming ID is unique); if another row exists then that is equivalent to your count returning greater than one. But you can't simply test two CLOB values for equality, you need to use dbms_lob.compare:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
);
Applying the row number filter is tricker, as you also can't use a CLOB in the analytic partition by clause. As André Schild suggested, you can use a hash; here passing the integer value 3, which is the equivalent of dbms_crypto.hash_sh1 (though in theory that could change in a future release!):
select id from (
select ID, ROW_NUMBER() over (partition by dbms_crypto.hash(LATEST_RECEIPT, 3)
order by ID) rownumber
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
)
)
where rownumber > 1;
It is of course possible to get a hash collision, and if that happened - you had two latest_receipt values which both appeared more than once and both hashed to the same value - then you could get too many rows back. That seems pretty unlikely, but it's something to consider.
So rather than ordering you can only look for rows which have the same lastest_receipt and a lower ID:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID < t1.ID
);
Again that assumes ID is unique. If it isn't then you could still use rowid instead, but you would have less control over which rows were found - the lowest rowid isn't necessarily the lowest ID. Presumably you're using this to dine rows to delete. If you actually don't mind which row you keep and which you delete then you could still do:
and t2.ROWID < t1.ROWID
But since you are currently ordering that probably isn't acceptable, and hashing might be preferable, despite the small risk.

Error using SQL 'With' clause

This is the query that I am using. I am getting an error with this query. 'syntax error near 'WITH' clause.
WITH RECURSIVE under_cust (affiliation_id, from_customer_id, to_customer_id, to_name, parent_customer_type, child_customer_type, level)
AS (SELECT af.affiliation_id,
from_customer_id,
to_customer_id,
to_name,
parent_customer_type,
child_customer_type,
0 LEVEL
FROM affiliation af,
customer c
WHERE to_customer_id <> from_customer_id
AND af.from_customer_id = c.customer_id
AND af.to_customer_id = 1000022559337
UNION ALL
SELECT af.affiliation_id,
af.from_customer_id,
af.to_customer_id,
af.to_name,
af.parent_customer_type,
af.child_customer_type,
under_cust.level + 1 LEVEL
FROM customer c,
affiliation af
JOIN under_cust smr
ON smr.from_customer_id = af.to_customer_id
WHERE af.from_customer_id = c.customer_id
) SELECT affiliation_id,
to_customer_id parent,
from_customer_id child,
to_name,
parent_customer_type,
child_customer_type,
level
FROM under_cust
Common table expressions and the WITH syntax were introduced only recently in sqlite version 3.8.3.
If you run the query on an older version, you get the syntax error.
Either upgrade your sqlite or make your code work without the WITH syntax.

PL-SQL Lastest record within a group: looking for alternate (PARTITION BY?) approaches

Using PL-SQL, I need to find the record with the lastest INVC_LN_ITEM_STAT_START_DT value within a group of records that share the same value for SHPMNT_LN_ITEM_KEY and RPT_PER_KEY.
How else might this be done? Are there analytical functions for this type of query?
SELECT
m1.*
FROM
HD_INVC_LN_ITEM_STAT m1
LEFT OUTER JOIN HD_INVC_LN_ITEM_STAT m2
ON (
m1.SHPMNT_LN_ITEM_KEY = m2.SHPMNT_LN_ITEM_KEY
AND m1.RPT_PER_KEY = m2.RPT_PER_KEY
AND m1.INVC_LN_ITEM_STAT_START_DT < m2.INVC_LN_ITEM_STAT_START_DT)
WHERE
m2.SHPMNT_LN_ITEM_KEY IS NULL
ORDER BY
m1.SHPMNT_LN_ITEM_KEY
,m1.RPT_PER_KEY
,m1.INVC_LN_ITEM_STAT_CD
,m1.INVC_LN_ITEM_STAT_START_DT
How about this?
SELECT
HD_INVC_LN_ITEM_STAT1.*
FROM
HD_INVC_LN_ITEM_STAT HD_INVC_LN_ITEM_STAT1
INNER JOIN
(
SELECT
HD_INVC_LN_ITEM_STAT2.SHPMNT_LN_ITEM_KEY
,HD_INVC_LN_ITEM_STAT2.RPT_PER_KEY
,MAX(HD_INVC_LN_ITEM_STAT2.INVC_LN_ITEM_STAT_START_DT) AS MAX_INVC_LN_ITEM_STAT_START_DT
FROM
HD_INVC_LN_ITEM_STAT HD_INVC_LN_ITEM_STAT2
GROUP BY
HD_INVC_LN_ITEM_STAT2.SHPMNT_LN_ITEM_KEY
,HD_INVC_LN_ITEM_STAT2.RPT_PER_KEY
) HD_INVC_LN_ITEM_STAT2
ON
HD_INVC_LN_ITEM_STAT1.SHPMNT_LN_ITEM_KEY = HD_INVC_LN_ITEM_STAT2.SHPMNT_LN_ITEM_KEY
AND HD_INVC_LN_ITEM_STAT1.RPT_PER_KEY
= HD_INVC_LN_ITEM_STAT2.RPT_PER_KEY
AND HD_INVC_LN_ITEM_STAT1.INVC_LN_ITEM_STAT_START_DT = HD_INVC_LN_ITEM_STAT2.MAX_INVC_LN_ITEM_STAT_START_DT
ORDER BY
HD_INVC_LN_ITEM_STAT1.SHPMNT_LN_ITEM_KEY
,HD_INVC_LN_ITEM_STAT1.RPT_PER_KEY
,HD_INVC_LN_ITEM_STAT1.INVC_LN_ITEM_STAT_CD
,HD_INVC_LN_ITEM_STAT1.INVC_LN_ITEM_STAT_START_DT;
It's longer but arguably more intuitive. I would like other people's opinions on which is more efficient.

record types that weren't found for a specific value in oracle query

I have this query
Select distinct p_id, p_date,p_city
from p_master
where p_a_id in(1,2,5,8,2,1,10,02)
and my IN clause contains 200 values. How do I get to know which ones weren't returned by the query. Each value in the IN clause may have a record in some cases they don't. I want to know all the records that weren't found for any selected p_a_id type.
Please help
This will do the trick but I'm sure there's an easier way to find this out :-)
with test1 as
(select '1,2,5,8,2,1,10,02' str from dual)
select * from (
select trim(x.column_value.extract('e/text()')) cols
from test1 t, table (xmlsequence(xmltype('<e><e>' || replace(t.str,',','</e><e>')|| '</e></e>').extract('e/e'))) x) cols
left outer join
(Select count(*), p_a_id from p_master where p_a_id in (1,2,5,8,2,1,10,02) group by p_a_id) p
on p.p_a_id = cols.cols
where p_a_id is null
;

Resources