Building a Postgresql View Over a View - sql

I've got a problem with one of my clients for OpenERP. I need to build a custom object for them, cause they have a problem where sometimes they get returned products after they have been sold. So I need to mark that as negative and subtract from the total sold from before.
SELECT sum(rsm1.product_qty_out) as "out"
, sum(rsm2.product_qty_in) as "in"
, rsm2.location_id
, rsm2.product_id
, rsm2.month
from report_stock_move as rsm1, report_stock_move as rsm2
where rsm2.location_id in (
select id
from stock_location
where "name" = 'Consumers'
)
and rsm1.location_id not in (
select id
from stock_location
where "name" = 'Consumers'
)
and rsm1.location_dest_id = rsm2.location_id
and rsm2.location_dest_id = rsm1.location_id
and rsm1.state = 'done' -- both are in done state
and rsm2.state = rsm1.state
group by rsm2.location_id, rsm2.month, rsm2.product_id
;
Anyone have any ideas? Also, I keep getting different results depending upon which table I use for the grouping. Why is that?
EDIT
Sorry, in my haste to solve this, I seem to have made my point clear. report_stock_moves that have a location_id of Consumers from a stock_location table need to be negative and summed up into report_stock_moves that have location_id of another place, but Consumer is the location_dest_id.
Example:
location_id = Some Place (actually ID of stock_location of course)
product_qty = 8
location_dest_id Consumers
date = 2012-07-02
location_id = Consumer
product_qty = 4
location_dest_id Some Place
date = 2012-07-04
Here are two record examples. They have different dates attached to them, so I'd probably want to take the max date, or the date of the returns(from Consumer to Some Place) if I want to union or group them so that when they are put together, i get 4, instead of say 8 if I didn't put them together and only look at what is going into Consumers.

In case anyone struggles with this for later. I'll paste my answer here. If anyone can help me explain about why union all in here seems to work, I'm all ears. I can do simple SQL, but I don't know much about how unions work, especially in case like this.
select max(total.id) as id,
sum(total.product_qty) as product_qty,
total.location_id as location_id,
total.date as "date",
total.year as year,
total.month as month,
total.day as day,
total.product_id as product_id
from (
-- From Consumer back to location
select -max(id) as id,
-sum(product_qty) as product_qty,
location_dest_id as location_id,
"date", year, month, day, product_id
from report_stock_move
where location_id in (
select id
from stock_location
where "name" = 'Consumers'
)
and "state" = 'done'
group by location_dest_id, "date", year, month, day, product_id
union all
-- From Location to Consumer
select max(id) as id,
sum(product_qty) as product_qty,
location_id, "date", year, month, day, product_id
from report_stock_move
where location_id not in (
select id
from stock_location
where "name" = 'Consumers'
)
and "state" = 'done'
and location_dest_id in (
select id
from stock_location
where "name" = 'Consumers'
)
group by location_id, "date", year, month, day, product_id
) as total
group by location_id, "date", year, month, day, product_id

Related

Prefered results

I am trying to list all DEPARTMENT_IDs with PRODUCT_IDs, first where PRODUCT_COST_STATUS = 1 but there are also data with where PRODUCT_COST_STATUS = 0. I prefer to list first "1"s and if not then "0"s with the latest date (this is another case for future) ... The code I wrote should give the expected result but it takes a lot of time to run query. I don't to want to list duplicate DEPARTMENT_ID.
Is there any way around ?
Thanks
SELECT PRODUCT_ID
,PRODUCT_COST_STATUS
,DEPARTMENT_ID FROM [PRODUCT_COST] PC
WHERE PRODUCT_COST_STATUS = 1
OR PRODUCT_ID NOT IN (SELECT PRODUCT_ID
FROM
[PRODUCT_COST]
where PRODUCT_COST_STATUS = 0
GROUP BY PRODUCT_ID,
PRODUCT_COST_STATUS,
DEPARTMENT_ID) GROUP BY PRODUCT_ID,
PRODUCT_COST_STATUS,
DEPARTMENT_ID
ORDER BY PRODUCT_ID,
DEPARTMENT_ID
I solved with the help of my friend and wanted to share here...
PRODUCT_COST_STATUS is a bit
SELECT PRODUCT_ID
,DEPARTMENT_ID
,LOCATION_ID
,max(cast(PRODUCT_COST_STATUS as int)) as maxpcs
,max(ACTION_DATE) as maxad
FROM [PRODUCT_COST]
group by
PRODUCT_ID, DEPARTMENT_ID,LOCATION_ID

Getting the date when selecting the same flag 4 times in a row

I have been presented with a problem, given three columns, customer ID, flag and date, where the flag resembles a country flag, for example USA. What I want to do is if a customer selects the same flag 4 times in a row, I want to get that customer ID, and date when he selected the same flag 4 times in a row.
This is my attempt at a solution:
Declare #SameFlagCount AS int = 0
Declare #currentFlag NVARCHAR(50)
Declare #nextFlag NVARCHAR(50)
SET #currentFlag = (Select Flag, customerID from FlagTable where countryName = ‘UK’)
While (#SameFlagCount < 4)
Begin
Set #nextFlag= (Select Flag, customerID
From FlagTable
Where countryName = “etc”)
IF (#currentFlag = #nextFlag)
Set #SameFlagCounter = #SameFlag + 1
Set #currentFlag = #nextFlag
Continue;
IF (#SameFlagCount = 4)
Select customerID, Date
From FlagTable
Else
Continue;
End
But I have been told this is too complex and I need to come up with a simpler solution using a mathematical function. I cannot imagine such a solution on my own.
Can someone provide me with insights at a simpler solution?
Thank you
EDIT:
I'll try to provide sample data, problem is it was a really vague specification: INSERT INTO FlagTable ( customerID, Flag, Date) VALUES (1, "UK", 4-10-2005), (2, "Us", 3-7-2005), (3, "INDIA", 22-4-2005). And I guess the desired result would be Customer ID 5 for example selected flag US on 3-7-2005
You can use lead():
select ft.*
from (select ft.*,
lead(date, 3) over (partition by customerid order by date) as date_3,
lead(date, 3) over (partition by customerid, flag order by date) as date_flag_3,
from flagtable ft
) ft
where date_3 = date_flag_3;
The idea is to look at the date three rows ahead for each customer. Then look at the date three rows ahead for each customer and flag. If the dates are the same, all the intervening dates have the same flag.

Query results half right after joining two tables

The following query resulted in correct results only for the inner query (post_engagement, website purchases) while all other numbers were incorrectly increased manyfold. Any ideas? Thanks.
Schema of the two tables:
Favorite_ads (id, campaign_id, campaign_name, objective, impressions, spend)
Actions (id, ads_id, action_type, value)
SELECT
f.campaign_id,
f.campaign_name,
f.objective,
SUM(f.impressions) AS Impressions,
SUM(f.spend) AS Spend,
SUM(a.post_engagement) AS "Post Engagement",
SUM(a.website_purchases) AS "Website Purchases"
FROM
favorite_ads f
LEFT JOIN (
SELECT
ads_id,
CASE WHEN action_type = 'post_engagement' THEN SUM(value) END AS
post_engagement,
CASE WHEN action_type = 'offsite_conversion.fb_pixel_purchase' THEN SUM(value) END AS website_purchases
FROM Actions a
GROUP BY ads_id, action_type
) a ON f.id = a.ads_id
WHERE date_trunc('month',f.date_start) = '2018-04-01 00:00:00' AND
date_trunc('month',f.date_stop) = '2018-04-01 00:00:00' --only get campaigns
that ran in April, 2018
GROUP BY f.campaign_id, campaign_name, objective
Order by campaign_id
Without knowing the actual table structure, constraints, dependencies and data, it's hard to tell, what the issue may be.
You already have some leads in the comments, which you should consider first.
For example you wrote, that this sub-query returns correct results:
SELECT ads_id,
CASE
WHEN action_type = 'post_engagement'
THEN SUM(value)
END AS post_engagement,
CASE
WHEN action_type = 'offsite_conversion.fb_pixel_purchase'
THEN SUM(value)
END AS website_purchases
FROM Actions a
GROUP BY ads_id, action_type
Is this one also giving correct results:
SELECT ads_id,
SUM(
CASE
WHEN action_type = 'post_engagement'
THEN value
END
) AS post_engagement,
SUM(
CASE
WHEN action_type = 'offsite_conversion.fb_pixel_purchase'
THEN value
END
) AS website_purchases
FROM Actions
GROUP BY ads_id
If so, then try replacing your sub-query with that one.
If you still have a problem, then I'd investigate if your join condition is correct, as it would seem, that for a campaign (campaign_id) you could probably have multiple entries with the same id, which will multiply the sub-query results - that depends on what is actually the primary key (or unique constraint) in the favorite_ads.

Datediff between two tables

I have those two tables
1-Add to queue table
TransID , ADD date
10 , 10/10/2012
11 , 14/10/2012
11 , 18/11/2012
11 , 25/12/2012
12 , 1/1/2013
2-Removed from queue table
TransID , Removed Date
10 , 15/1/2013
11 , 12/12/2012
11 , 13/1/2013
11 , 20/1/2013
The TansID is the key between the two tables , and I can't modify those tables, what I want is to query the amount of time each transaction spent in the queue
It's easy when there is one item in each table , but when the item get queued more than once how do I calculate that?
Assuming the order TransIDs are entered into the Add table is the same order they are removed, you can use the following:
WITH OrderedAdds AS
( SELECT TransID,
AddDate,
[RowNumber] = ROW_NUMBER() OVER(PARTITION BY TransID ORDER BY AddDate)
FROM AddTable
), OrderedRemoves AS
( SELECT TransID,
RemovedDate,
[RowNumber] = ROW_NUMBER() OVER(PARTITION BY TransID ORDER BY RemovedDate)
FROM RemoveTable
)
SELECT OrderedAdds.TransID,
OrderedAdds.AddDate,
OrderedRemoves.RemovedDate,
[DaysInQueue] = DATEDIFF(DAY, OrderedAdds.AddDate, ISNULL(OrderedRemoves.RemovedDate, CURRENT_TIMESTAMP))
FROM OrderedAdds
LEFT JOIN OrderedRemoves
ON OrderedAdds.TransID = OrderedRemoves.TransID
AND OrderedAdds.RowNumber = OrderedRemoves.RowNumber;
The key part is that each record gets a rownumber based on the transaction id and the date it was entered, you can then join on both rownumber and transID to stop any cross joining.
Example on SQL Fiddle
DISCLAIMER: There is probably problem with this, but i hope to send you in one possible direction. Make sure to expect problems.
You can try in the following direction (which might work in some way depending on your system, version, etc) :
SELECT transId, (sum(add_date_sum) - sum(remove_date_sum)) / (1000*60*60*24)
FROM
(
SELECT transId, (SUM(UNIX_TIMESTAMP(add_date)) as add_date_sum, 0 as remove_date_sum
FROM add_to_queue
GROUP BY transId
UNION ALL
SELECT transId, 0 as add_date_sum, (SUM(UNIX_TIMESTAMP(remove_date)) as remove_date_sum
FROM remove_from_queue
GROUP BY transId
)
GROUP BY transId;
A bit of explanation: as far as I know, you cannot sum dates, but you can convert them to some sort of timestamps. Check if UNIX_TIMESTAMPS works for you, or figure out something else. Then you can sum in each table, create union by conveniently leaving the other one as zeto and then subtracting the union query.
As for that devision in the end of first SELECT, UNIT_TIMESTAMP throws out miliseconds, you devide to get days - or whatever it is that you want.
This all said - I would probably solve this using a stored procedure or some client script. SQL is not a weapon for every battle. Making two separate queries can be much simpler.
Answer 2: after your comments. (As a side note, some of your dates 15/1/2013,13/1/2013 do not represent proper date formats )
select transId, sum(numberOfDays) totalQueueTime
from (
select a.transId,
datediff(day,a.addDate,isnull(r.removeDate,a.addDate)) numberOfDays
from AddTable a left join RemoveTable r on a.transId = r.transId
order by a.transId, a.addDate, r.removeDate
) X
group by transId
Answer 1: before your comments
Assuming that there won't be a new record added unless it is being removed. Also note following query will bring numberOfDays as zero for unremoved records;
select a.transId, a.addDate, r.removeDate,
datediff(day,a.addDate,isnull(r.removeDate,a.addDate)) numberOfDays
from AddTable a left join RemoveTable r on a.transId = r.transId
order by a.transId, a.addDate, r.removeDate

SQL: Using UNION

Here is the question and database info.
Use the UNION command to prepare a full statement for customer 'C001' - it should be laid out as follows. (Note that the values shown below are not correct.) You may be able to use '' or NULL for blank values - if necessary use 0.
Here is a link to the webpage with the database info. http://sqlzoo.net/5_0.htm or see the image below.
Here is what I have tried:
SELECT sdate AS LineDate, "delivery" AS LEGEND, price*quantity AS Total,"" AS Amount
FROM shipped
JOIN product ON (shipped.product=product.id)
WHERE badguy='C001'
UNION
SELECT rdate,notes, "",receipt.amount
FROM receipt
WHERE badguy='C001'
Here is what I get back:
Wrong Answer. The correct answer has 5 row(s).
The amounts don't seem right in the amount column and I can't figure out how to order the data by the date since it is using two different date columns (sdate and rdate which are UNIONED).
Looks like the data in the example is being aggregated by date and charge type using group by, that's why you are getting too many rows.
Also, you can sort by the alias of the column (LineDate) and the order by clause will apply to all the rows in the union.
SELECT sdate AS LineDate, "delivery" AS LEGEND, SUM(price*quantity) AS Total,"" AS Amount
FROM shipped
JOIN product ON (shipped.product=product.id)
WHERE badguy='C001'
GROUP BY sdate
UNION
SELECT rdate, notes, "",receipt.amount
FROM receipt
WHERE badguy='C001'
ORDER BY LineDate
It's usually easiest to develop each part of the union separately. Pay attention to the use of "null" to separate the monetary columns. The first select gets to name the columns.
select s.sdate as tr_date, 'Delivery' as type, sum((s.quantity * p.price)) as extended_price, null as amount
from shipped s
inner join product p on p.id = s.product
where badguy = 'C001'
group by s.sdate
union all
select rdate, notes, null, sum(amount)
from receipt
where badguy = 'C001'
group by rdate, notes
order by tr_date