On Wed, Dec 13, 2017 at 10:24 PM, Leo Simmons <l...@si14.io> wrote:
> I have a table `special_product_list` and a table
> `special_product_historical_details_list` that holds information of products
> over time, where there's a record in the history table every day for every
> product.
>
> I want to write a query to select all products from `special_product_list`
> that meet the two following conditions:
>     - the product's most recent `special_product_historical_details_list`
> entry has `available=true`
>     - at least one other `special_product_historical_details_list` entry for
> that product except the most recent one has `available=false`
>
> if the following were the most recent
> `special_product_historical_details_list` entries for a product, only in the
> first situation would I want that product to be returned from the query
>
> p_id | date       | is_available
> ----------------------------
> 1   | 2017-12-13 | true
> 1   | 2017-12-12 | false
> 1   | 2017-12-11 | true
> 1   | 2017-12-10 | true
>
> p_id | date      | is_available
>  -------------------------------
>  1   |2017-12-13 | false
>  1   |2017-12-12 | true
>  1   |2017-12-11 | false
>  1   |2017-12-10 | false
>
> p_id | date      | is_available
>  -------------------------------
>  1   |2017-12-13 | true
>  1   |2017-12-12 | true
>  1   |2017-12-11 | true
>  1   |2017-12-10 | true
>

I would approach this by figuring out the SQL first, and then
translate to SQLAlchemy afterwards.

To match your first condition (most recent row has 'available=true'),
you need a way to find the most recent row for each p_id. Here's one
way (all untested):

SELECT p_id, MAX(date) AS max_date
FROM special_product_historical_details_list
GROUP BY p_id

To find rows where the most recent entry has 'available=true', you
would combine the above subquery with a join back to the
special_product_historical_details_list table again, something like:

SELECT *
FROM special_product_historical_details_list sphdl
INNER JOIN (
    SELECT p_id, MAX(date) AS max_date
    FROM special_product_historical_details_list
    GROUP BY p_id
) lastrows ON (lastrows.p_id = sphdl.p_id AND lastrows.max_date = sphdl.date)
WHERE sphdl.is_available = 'true'

(This assumes that each product has unique date values - you'll get
confusing results if a product ever has more than one row with the
same date)

For your second condition, you want to know if a product has any
"false" entries. (There's no need to include the "except for the most
recent entry" part, because you're going to combine this condition
with the previous one, which already asserts that the most recent
entry is "true"). There are a few ways of writing this (eg. using
EXISTS or ANY with a correlated subquery) but I'm not sure of the
performance characteristics. Here's one way:

SELECT DISTINCT p_id
FROM special_product_historical_details_list
WHERE available = 'false'

The combined query would look something like:

SELECT *
FROM special_product_historical_details_list sphdl
INNER JOIN (
    SELECT p_id, MAX(date) AS max_date
    FROM special_product_historical_details_list
    GROUP BY p_id
) lastrows ON (lastrows.p_id = sphdl.p_id AND lastrows.max_date = sphdl.date)
INNER JOIN (
    SELECT DISTINCT p_id
    FROM special_product_historical_details_list
    WHERE available = 'false'
) havefalse ON sphdl.p_id = havefalse.p_id
WHERE sphdl.is_available = 'true'

> Is there a way I could write this in SqlAlchemy?

You ought to be able to build up the above SQL incrementally using the
Query.subquery() method. See the example at:

http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#using-subqueries

If you wanted to use the EXISTS version of the second condition, you
would need something like:

http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#using-exists

Hope that helps,

Simon

>
> Here are my models:
>
> class SpecialProduct(Product):
>     __tablename__ =  'special_product_list'
>     special_id = db.Column(String(64), nullable=False, index=True,
> primary_key=True)
>     history = relationship("SpecialProductHistoricalDetails",
>                             back_populates='special_product',
>
> foreign_keys='SpecialProductHistoricalDetails.special_id')
>     most_recent_historical_details_id = db.Column(Integer,
>
> ForeignKey('special_product_historical_details_list.id'))
>     most_recent_historical_details_entry =
> relationship("SpecialProductHistoricalDetails",
>                             uselist=False,
> foreign_keys=[most_recent_historical_details_id])
>
> class SpecialProductReviewsHistoricalDetails(db.Model):
>     __tablename__ = 'special_product_historical_details_list'
>     id = db.Column(Integer, primary_key=True)
>     special_id = db.Column(String(64),
> ForeignKey('special_product_list.special_id'), nullable=False)
>     special_product = relationship("SpecialProduct", uselist=False,
> foreign_keys=[special_id])
>     time_updated = db.Column(TIMESTAMP, default=func.now(),
>                              onupdate=func.now(),
> server_default=text("CURRENT_TIMESTAMP"))
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to