How can I add selected columns to a select statement in sqlalchemy? - sqlalchemy

If I have the following models:
Base = declarative_base()
class Customer(Base):
__tablename__ = 'Customers'
Id = Column(Integer, primary_key=True)
Name = Column(String(255))
class Order(Base):
__tablename__ = 'Orders'
Id = Column(Integer, primary_key=True)
CustomerId = Column(Integer)
Price = Column(Integer)
First, I create a select statement by
stmt1 = select(Customer)
Then, I want to add some info about orders to it, so I create another statement:
stmt1 = select(Customer)
stmt2 = select(func.row_number().over(partition_by=Order.CustomerId, order_by=Order.Price).label("rn"), Order)
stmt2_subq= stmt2.subquery()
stmt3 = stmt1.join(stmt2_subq, Customer.Id == stmt2_subq.c.CustomerId)
The problem is stmt3 dose not contain Order.Price.
How can I add Order.Price to stmt3?
I can do it by:
stmt4 = select(stmt3.subquery(), Order.Price)
But in this way, it will create a stupid select like:
SELECT anon_1.`Id`,
anon_1.`Name`,
`Orders`.`Price`
FROM
(SELECT `Customers`.`Id` AS `Id`,
`Customers`.`Name` AS `Name`
FROM `Customers`
INNER JOIN
(SELECT row_number() OVER (PARTITION BY `Orders`.`CustomerId`
ORDER BY `Orders`.`Price`) AS rn,
`Orders`.`Id` AS `Id`,
`Orders`.`CustomerId` AS `CustomerId`,
`Orders`.`Price` AS `Price`
FROM `Orders`) AS anon_2 ON `Customers`.`Id` = anon_2.`CustomerId`) AS anon_1,
`Orders`
What I want is:
SELECT `Customers`.`Id` AS `Id`,
`Customers`.`Name` AS `Name`,
anon_2.Price
FROM `Customers`
INNER JOIN
(SELECT row_number() OVER (PARTITION BY `Orders`.`CustomerId`
ORDER BY `Orders`.`Price`) AS rn,
`Orders`.`Id` AS `Id`,
`Orders`.`CustomerId` AS `CustomerId`,
`Orders`.`Price` AS `Price`
FROM `Orders`) AS anon_2 ON `Customers`.`Id` = anon_2.`CustomerId`
How can I get this if I already have a very complex stmt1 in my real code and do not want to modify the stmt1's select args?

Related

MySQL Getting row numbers of table inside subquery

I have one query where I need to have number of rows of an table by particular ID:
SELECT
(CAST(`ot`.`value` AS DECIMAL(6,2))) AS `value`,
`op`.`orders_id`,
(
SELECT
COUNT(1) AS `total`
FROM
(
SELECT
`op2`.`concert_date`
FROM
`orders_products` `op2`
WHERE
`op2`.`orders_id` = `op`.`orders_id`
AND
`op2`.`concert_date` <> ''
GROUP BY CONCAT(`op2`.`concert_date`,' ',`op2`.`concert_time`)
) AS `e`
) AS `devider`
FROM
`categories` `c`
JOIN `products` `p` ON `p`.`section_id` = `c`.`section_id`
JOIN `orders_products` `op` ON `op`.`products_id` = `p`.`products_id`
JOIN `orders_total` `ot` ON `ot`.`orders_id` = `op`.`orders_id`
WHERE
`c`.`section_id` = 25
AND
`p`.`product_type` IN ('P')
AND
`ot`.`class` IN ('ot_shipping')
GROUP BY `op`.`orders_id`
The main problem is that I getting error
#1054 - Unknown column 'op.orders_id' in 'where clause'
And can't run this. I have separated query in my loop but that made performance issue and want to push it in one query. Any idea?
try removing sub-sub query and use COUNT(DISTINCT ..)
SELECT
(CAST(`ot`.`value` AS DECIMAL(6,2))) AS `value`,
`op`.`orders_id`,
(
SELECT
COUNT(DISTINCT CONCAT(`op2`.`concert_date`,' ',`op2`.`concert_time`))
FROM
`orders_products` `op2`
WHERE
`op2`.`orders_id` = `op`.`orders_id`
AND
`op2`.`concert_date` <> ''
) AS `devider`
FROM
`categories` `c`
JOIN `products` `p` ON `p`.`section_id` = `c`.`section_id`
JOIN `orders_products` `op` ON `op`.`products_id` = `p`.`products_id`
JOIN `orders_total` `ot` ON `ot`.`orders_id` = `op`.`orders_id`
WHERE
`c`.`section_id` = 25
AND
`p`.`product_type` IN ('P')
AND
`ot`.`class` IN ('ot_shipping')
GROUP BY `op`.`orders_id`
and you don't even need subquery or concat as long concert_date or concert_time is null
SELECT
(CAST(`ot`.`value` AS DECIMAL(6,2))) AS `value`,
`op`.`orders_id`,
COUNT(DISTINCT `op`.`concert_date`, `op`.`concert_time`) AS `devider`
FROM
`categories` `c`
JOIN `products` `p` ON `p`.`section_id` = `c`.`section_id`
LEFT JOIN `orders_products` `op` ON `op`.`products_id` = `p`.`products_id`
JOIN `orders_total` `ot` ON `ot`.`orders_id` = `op`.`orders_id`
WHERE
`c`.`section_id` = 25
AND
`p`.`product_type` IN ('P')
AND
`ot`.`class` IN ('ot_shipping')
GROUP BY `op`.`orders_id`
In this subquery:
SELECT
`op2`.`concert_date`
FROM
`orders_products` `op2`
WHERE
`op2`.`orders_id` = `op`.`orders_id`
AND
`op2`.`concert_date` <> ''
GROUP BY CONCAT(`op2`.`concert_date`,' ',`op2`.`concert_time`)
you are not selecting op as table.
Should be:
SELECT
`op2`.`concert_date`
FROM
`orders_products` `op2`, `orders_products` `op`
WHERE
`op2`.`orders_id` = `op`.`orders_id`
AND
`op2`.`concert_date` <> ''
GROUP BY CONCAT(`op2`.`concert_date`,' ',`op2`.`concert_time`)
Beside this you are making an implicit join while you should use explicit even here and the subquery should become:
SELECT
`op2`.`concert_date`
FROM
`orders_products` `op2` JOIN `orders_products` `op`
ON
`op2`.`orders_id` = `op`.`orders_id`
WHERE
`op2`.`concert_date` <> ''
GROUP BY CONCAT(`op2`.`concert_date`,' ',`op2`.`concert_time`)

Converting complex MySQL query to UPDATE statement

I have a quite complex Mysql query:
SELECT
metabase_field.description, rows_to_copy.description,
metabase_field.display_name, rows_to_copy.display_name
FROM
metabase_field LEFT JOIN (select id as table_id, name as t_name from metabase_table) metabase_field_table ON metabase_field.table_id=metabase_field_table.table_id,
(
SELECT metabase_field.name as name, metabase_field_table.t_name as t_name, metabase_field.display_name as display_name, metabase_field.description as description, metabase_field.special_type as type
FROM metabase_field
LEFT JOIN (select id as table_id, name as t_name, db_id, active, visibility_type from metabase_table) metabase_field_table ON metabase_field.table_id = metabase_field_table.table_id
LEFT JOIN metabase_database metabase_field_table_database ON metabase_field_table.db_id = metabase_field_table_database.id
where metabase_field_table.active=1 and metabase_field_table.visibility_type is null and metabase_field_table_database.name = 'Prod'
) as rows_to_copy
WHERE
metabase_field_table.table_id IN (
SELECT distinct(metabase_table.id) as ids
from metabase_table
LEFT JOIN metabase_database metabase_table_database ON metabase_table.db_id = metabase_table_database.id
where metabase_table_database.name = 'Dev' and metabase_table.active=1 and metabase_table.visibility_type is null
)
and metabase_field_table.t_name = rows_to_copy.t_name
and metabase_field.name = rows_to_copy.name
This returns a table looking a bit like this:
+-------------+-------------+--------------+--------------+
| description | description | display_name | display_name |
+-------------+-------------+--------------+--------------+
| NULL | to copy | Application | Application |
Without going to deep into details, I just wanted to copty results from rows_to_copy to metabase_field fields, which means to set:
metabase_field.description = rows_to_copy.description,
metabase_field.display_name = rows_to_copy.display_name
I tried to just change SELECT to UPDATE:
UPDATE
metabase_field LEFT JOIN (select id as table_id, name as t_name from metabase_table) metabase_field_table ON metabase_field.table_id=metabase_field_table.table_id,
(
SELECT metabase_field.name as name, metabase_field_table.t_name as t_name, metabase_field.display_name as display_name, metabase_field.description as description, metabase_field.special_type as type
FROM metabase_field
LEFT JOIN (select id as table_id, name as t_name, db_id, active, visibility_type from metabase_table) metabase_field_table ON metabase_field.table_id = metabase_field_table.table_id
LEFT JOIN metabase_database metabase_field_table_database ON metabase_field_table.db_id = metabase_field_table_database.id
where metabase_field_table.active=1 and metabase_field_table.visibility_type is null and metabase_field_table_database.name = 'Prod'
) as rows_to_copy
SET
metabase_field.description = rows_to_copy.description,
metabase_field.display_name = rows_to_copy.display_name
WHERE
metabase_field_table.table_id IN (
SELECT distinct(metabase_table.id) as ids
from metabase_table
LEFT JOIN metabase_database metabase_table_database ON metabase_table.db_id = metabase_table_database.id
where metabase_table_database.name = 'Dev' and metabase_table.active=1 and metabase_table.visibility_type is null
)
and metabase_field_table.t_name = rows_to_copy.t_name
and metabase_field.name = rows_to_copy.name
;
But this query doesn't seem to change anything - the results stay the way they were. Is there any simple way to make it work?
After some formating I realize if almost imposible keep track of table names. use shorter and different alias to avoid bugs. I try to improve the rest of the code but too much work
I think the problem is the update. Update table should have the alias MF, and you SET MF.<field>
If that doesnt work, remove the WHERE and see if you UPDATE something. Of course only if you can do testing on those tables.
If that doesnt work, start with a single table and add one join at each time.
UPDATE metabase_field MF -- main alias for table to update.
LEFT JOIN (SELECT id as table_id, name as t_name
FROM metabase_table) MT1
ON MF.table_id = MT1.table_id
CROSS JOIN ( SELECT MF1.name as name,
MT2.t_name as t_name,
MF1.display_name as display_name,
MF1.description as description,
MF1.special_type as type
FROM metabase_field MF1
LEFT JOIN (SELECT id as table_id,
name as t_name,
db_id, active,
visibility_type
from metabase_table) MT2
ON MF1.table_id = MT2.table_id
LEFT JOIN metabase_database MD
ON MT2.db_id = MD.id
WHERE MT2.active = 1
and MT2.visibility_type is null
and MD.name = 'Prod'
) as rows_to_copy
SET
MF.description = rows_to_copy.description,
MF.display_name = rows_to_copy.display_name
WHERE
metabase_field_table.table_id IN (
SELECT distinct(metabase_table.id) as ids
from metabase_table
LEFT JOIN metabase_database MB
ON MT2.db_id = MB.id
WHERE MB.name = 'Dev'
and MT2.active=1
and MT2.visibility_type is null
)
and metabase_field_table.t_name = rows_to_copy.t_name
and metabase_field.name = rows_to_copy.name
;
Update metabase_field set metabase_field.description=
rows_to_copy.description, metabase_field.display_name=
rows_to_copy.display_name LEFT JOIN (select id as table_id, name as
t_name from metabase_table) metabase_field_table ON
metabase_field.table_id=metabase_field_table.table_id, ( SELECT
metabase_field.name as name, metabase_field_table.t_name as t_name,
metabase_field.display_name as display_name,metabase_field.description as description, metabase_field.special_type
as type FROM metabase_field LEFT JOIN (select id as table_id, name as
t_name, db_id, active, visibility_type from metabase_table)
metabase_field_table ON metabase_field.table_id =
metabase_field_table.table_id LEFT JOIN metabase_database
metabase_field_table_database ON metabase_field_table.db_id =
metabase_field_table_database.id where metabase_field_table.active=1
and metabase_field_table.visibility_type is null and
metabase_field_table_database.name = 'Prod' ) as rows_to_copy WHERE
metabase_field_table.table_id IN ( SELECT distinct(metabase_table.id)
as ids from metabase_table LEFT JOIN metabase_database
metabase_table_database ON metabase_table.db_id =
metabase_table_database.id where metabase_table_database.name = 'Dev'
and metabase_table.active=1 and metabase_table.visibility_type is null
) and metabase_field_table.t_name = rows_to_copy.t_name and
metabase_field.name = rows_to_copy.name
Try this

Insert in to a new table results from multiple queries

I have these queries and I want to insert these results in to a single temporary table. How Can I do that?
select date(max(created_date)) AS 'Last Shipped Date',
sum(item_count) AS 'Last Shipped Units'
from order
where is_shipped = 1
AND date(shipped_date) = (select date(max(shipped_date)) from order);
select
count(distinct o.key) AS 'ACTIVE ',
count(od.ean) AS 'Active_ Units'
from order o
left join order_details od on o.id = od. order_details
Where o.is_active = 1;
select count(distinct order_key) AS 'Total_Orders_Shipped_Yesterday',
sum(item_count) AS 'Total_units_Shipped_yesterday'
from order
where datediff(curdate(), modified_date)=1
AND is_shipped =1;
select count(distinct liquidation_order_id) AS 'orders cancelled',
count(ean) AS 'Units cancelled'
from order_details
where datediff(curdate(), modified_date)=1
AND order_details_status_ =4;
There may be a way to do it in one query, but it will be complicated. It's easier to just do a series of UPDATE queries that fill in the appropriate columns in the table by joining with the other queries that calculate the values.
CREATE TEMPORARY TABLE tempTable (
`Last Shipped Date` DATE,
`Last Shipped Units` INT,
Active INT,
Active_Units INT,
Total_Orders_Shipped_Yesterday INT,
Total_units_Shipped_yesterday INT,
`orders cancelled` INT,
`Units cancelled` INT);
INSERT INTO tempTable (`Last Shipped Date`, `Last Shipped Units`)
select date(max(created_date)) AS 'Last Shipped Date',
sum(item_count) AS 'Last Shipped Units'
from order
where is_shipped = 1
AND date(shipped_date) = (select date(max(shipped_date)) from order);
UPDATE tempTable AS t
JOIN order o
left join order_details od on o.id = od. order_details
SET t.Active = count(distinct o.key), t.Active_Units = count(od.ean)
Where o.is_active = 1;
UPDATE tempTable AS t
JOIN order
SET t.Total_Orders_Shipped_Yesterday = count(distinct order_key),
t.Total_units_Shipped_yesterday = SUM(item_count)
where datediff(curdate(), modified_date)=1
AND is_shipped =1;
UPDATE tempTable AS t
JOIN order_details
SET t.`orders cancelled` = count(distinct liquidation_order_id),
t.`Units cancelled` = COUNT(ean)
where datediff(curdate(), modified_date)=1
AND order_details_status_ =4;

view select cannot contain from clause

I have a SQL Query. When I run it executes successfully, but when i was trying to create a view with the query it shows create a view with each subquery.
SOS.
SELECT `product_category_vendor_id`,
ct1.category_id,
ct1.category_name,
c1.`product_id`,
p1.product_name,
c1.`vendor_id`,
v1.vendor_name,
c1.`product_purchase_price`,
c1.product_vendor_qty,
d1.available
FROM (SELECT `product_category_vendor_id`,
a1.`product_id`,
a1.`vendor_id`,
a1.`product_purchase_price`,
b2.product_vendor_qty
FROM (SELECT `product_category_vendor_id`,
`product_id`,
`vendor_id`,
`product_purchase_price`
FROM tbl_inksand_product_category_vendor
WHERE product_category_vendor_id IN
(SELECT
Max(product_category_vendor_id) AS
`Product_Category_Vendor_Id`
FROM
tbl_inksand_product_category_vendor
WHERE (
product_category_vendor_status =
'A' )
GROUP BY product_id,
`vendor_id`
ORDER BY `product_id`))
AS a1
JOIN (SELECT `product_id`,
`vendor_id`,
Sum(`product_vendor_qty`) AS Product_Vendor_Qty
FROM tbl_inksand_product_category_vendor
GROUP BY product_id,
`vendor_id`
ORDER BY `product_id`) b2
ON a1.`product_id` = b2.`product_id`
AND a1.`vendor_id` = b2.`vendor_id`) c1
JOIN (SELECT `product_id`,
`vendor_id`,
Count(`item_id`) AS Available
FROM `tbl_inksand_item_dtls`
WHERE `product_item_status` = 'Not Sale'
GROUP BY `product_id`,
`vendor_id`) d1
ON c1.`product_id` = d1.`product_id`
AND c1.`vendor_id` = d1.`vendor_id`
JOIN tbl_inksand_vendor AS v1
ON c1.`vendor_id` = v1.`vendor_id`
JOIN tbl_inksand_product p1
ON p1.product_id = c1.`product_id`
JOIN tbl_inksand_category ct1
ON ct1.category_id = p1.category_id
Subqueries cannot be used in the FROM clause of a view.
Source: https://dev.mysql.com/doc/refman/5.0/en/view-restrictions.html
So yeah it was the right reccomendation to create seperate views.

Update in a join with sub-query

UPDATE item t
INNER JOIN ( SELECT
item_name,
MAX( item_keyword ) AS item_keyword
FROM item
WHERE ca_id2 = '2010'
GROUP BY item_name
) s ON t.item_name = s.item_name
SET t.item_keyword = s.item_keyword
WHERE t.ca_id2 ='3010'
The error is:
1064 - You have an error in your SQL syntax. Check the manual that
corresponds to your MySQL server version for the right syntax to
use near 'SELECT item_name, max(item_keyword) AS item_keyword
FROM item
MySQL version 4.0.22 What is the reason for the failure?
I tried to construct your query based on
http://www.xaprb.com/blog/2006/06/23/how-to-select-from-an-update-target-in-mysql/
UPDATE item t
SET t.item_keyword =
( SELECT MAX( i.item_keyword ) AS item_keyword
FROM ( SELECT item_keyword, item_name, cd_id2 FROM item ) AS i
WHERE t.item_name = i.item_name
AND i.cd_id2 = '2010'
GROUP BY i.item_name
)
WHERE t.ca_id2 ='3010'
Or you can try this version too
UPDATE item t
INNER JOIN ( SELECT item_keyword, item_name, cd_id2 FROM item ) AS i
SET t.item_keyword =
( SELECT MAX( i.item_keyword ) AS item_keyword
FROM i
WHERE t.item_name = i.item_name
AND i.cd_id2 = '2010'
GROUP BY i.item_name
)
WHERE t.ca_id2 ='3010'
Sorry, I don't have MySQL 4 around, so you have to test the above queries.
Other readings:
http://dev.mysql.com/doc/refman/4.1/en/update.html
http://dev.mysql.com/doc/refman/4.1/en/rewriting-subqueries.html
If the above queries don't work, you can try using a temporary table instead of the subselect:
http://sqlfiddle.com/#!2/13ccb/1
CREATE TEMPORARY TABLE s
SELECT item_name,
MAX( item_keyword ) AS item_keyword
FROM item
WHERE cd_id2 = '2010'
AND item.item_name IN ( SELECT item_name FROM item WHERE item.ca_id2='3010')
GROUP BY item_name;
UPDATE item t
INNER JOIN s ON t.item_name = s.item_name
SET t.item_keyword = s.item_keyword
WHERE t.ca_id2 ='3010';
DROP TEMPORARY TABLE s;