SUBQUERY WITH SAME TABLE - sql

I have a table that contains :
ID_Magasin: 001 for wharehouse, c01 for a magasin1, c02 for magasin 2, ..
Qte_Physique: the quantity number of an article
id_article: code article
lib_article: article designation
I want a query first, bring all the quantity of articles for each magasin.
second, in the same query I want to add a new column to display the quantity of the same article in the datawherhouse.

If I understand correctly, you can do this with a window function:
select d.*
from (select d.*,
sum(case when id_magasin = '001' then Qte_Physique else 0 end) over (partition by id_article) as wharehouse_qte
from dispo d
) d
where id_magasin <> '001';

Your subquery is not correlated to the main query. I.e. it is lacking the condition which product to look at.
select
gq_depot,
gq_article,
gq_physique,
(
select warehouse.gq_physique
from dispo warehouse
where warehouse.gq_depot = '001'
and warehouse.gq_article = magasin.gq_article
) as wh_physique
from dispo magasin
where gq_depot <> '001'
order by gq_depot, gq_article;
You can do the same with a join:
select
magasin.gq_depot,
magasin.gq_article,
magasin.gq_physique,
warehouse.gq_physique as wh_physique
from dispo magasin
left join dispo warehouse on warehouse.gq_article = magasin.gq_article
and warehouse.gq_depot = '001'
where magasin.gq_depot <> '001'
order by magasin.gq_depot, magasin.gq_article;
For readability you may use WITH clauses, if your DBMS features them:
with warehouse as (select * from dispo where gq_depot = '001')
, magasin as (select * from dispo where gq_depot <> '001')
select
magasin.gq_depot,
magasin.gq_article,
magasin.gq_physique,
warehouse.gq_physique as wh_physique
from magasin
left join warehouse on warehouse.gq_article = magasin.gq_article
order by magasin.gq_depot, magasin.gq_article;
Above queries only work correctly, if gq_depot + gq_article are unique in the table (e.g. constituting the primary key). Otherwise you'd need aggregation to get total gq_physique per gq_depot + gq_article.

Related

Postgresql: Query using multiple attributes

I'm trying to create a query with Postgresql. Unfortunately, the attributes in the attribute_table are listed as rows instead of columns which makes it harder to pull. I want to pull a count based on the three attributes I have listed below (1000 = gender - 2 = female, 1001 = age group - 5 = 55-64, 1002 = household size = 1). How do I adjust this query so that it only gives me one row vs three rows of the same personal_ID? Also when I use this query, it doesn't pull any values but if I put only 1 attribute it works.
select sa.country_id ,count(distinct sa.personal_id )
from study_table sa ,attribute_table a
where sa.country_id =a.country_id and sa.personal_id =a.personal_id
and to_char(sa.mailing_date,'yyyy-MM')='2021-01'
and attribute_id =1000 and a.attribute_number =2
and attribute_id =1001 and a.attribute_number =5
and attribute_id =1002 and a.attribute_number =1
and study_type ='Wave'
and status not in ('NEW','EXCLUDED','ERROR')
group by sa.country_id
First, use proper, explicit, standard JOIN syntax.
Second, your WHERE conditions are contradictory. You need ORs . . . and then a HAVING for the final filtering:
select sa.country_id ,count(distinct sa.personal_id )
from study_table sa join
attribute_table a
on sa.country_id = a.country_id and
sa.personal_id = a.personal_id
where to_char(sa.mailing_date,'yyyy-MM') = '2021-01' and
( (attribute_id = 1000 and a.attribute_number = 2) or
(attribute_id = 1001 and a.attribute_number = 5) or
(attribute_id = 1002 and a.attribute_number = 1)
) and
study_type ='Wave'
status not in ('NEW','EXCLUDED','ERROR')
group by sa.country_id
having count(distinct attribute_id) = 3;
It seems your attribute_table follows an Entity-Attribute-Value. (IMHO that is an extremely bad bad plan - but if that is what you got that is what you deal with). So to get/validate/set 3 attributes you need to reference that table 3 times in the query -once for each attribute.
select sa.country_id
, count(distinct sa.personal_id )
from study_table sa
join ( select g.personal_id
from attribute_table g
join attribute_table ag on ag.personal_id = ag.personal_id
join attribute_table hs on hs.personal_id = hs.personal_id
where 1=1
and (g.attribute_id =1000 and g.attribute_number =2)
and (ag.attribute_id =1001 and ag.attribute_number =5)
and (hs.attribute_id =1002 and hs.attribute_number =1)
) sf
on sf.personal_id = sa.personal_id
where 1=1
and to_char(sa.mailing_date,'yyyy-mm')='2021-01'
and sa.study_type ='Wave'
and sa.status not in ('NEW','EXCLUDED','ERROR')
group by sa.country_id;
The above of course assumes the column names study_type and status originate in the study table - seems likely.
But if not you will need 2 more references to the attribute table.
Hint: Always use tables aliases on ALL column references.

Joining multiple CTEs

I am working on a database of a large retail store.
I have to query data from multiple tables to get numbers such as revenue, raw proceeds and compare different time periods.
Most of it is quite easy but I was struggling to work out a way of joining multiple CTEs.
I made a fiddle so you know what I am talking about.
I simplified the structure a lot and left out quite a few columns in the subqueries because they do not matter in this case.
As you can see every row in every table has country and brand in it.
The final query has to be grouped by those.
What I first tried was to FULL JOIN all the tables, but that didn't work in some cases as you can see here: SQLfiddle #1. Note the two last rows which did not group correctly.
Select Coalesce(incoming.country, revenue.country, revcompare.country,
openord.country) As country,
Coalesce(incoming.brand, revenue.brand, revcompare.brand,
openord.brand) As brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
From incoming
Full Join openord On openord.country = incoming.country And
openord.brand = incoming.brand
Full Join revenue On revenue.country = incoming.country And
revenue.brand = incoming.brand
Full Join revcompare On revcompare.country = incoming.country And
revcompare.brand = incoming.brand
Group By incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare,
incoming.country,
revenue.country,
openord.country,
revcompare.country,
incoming.brand,
revenue.brand,
revcompare.brand,
openord.brand
Order By country,
brand
I then rewrote the query keeping all the CTEs. I added another CTE (basis) which UNIONs all the possible country and brand combinations and left joined on that one.
Now it works fine (check it out here -> SQLfiddle #2) but it just seems so complicated. Isn't there an easier way to achieve this? The only thing I probably won't be able to change are the CTEs as in real life they are way more complex.
WITH basis AS (
SELECT Country, Brand FROM incoming
UNION
SELECT Country, Brand FROM openord
UNION
SELECT Country, Brand FROM revenue
UNION
SELECT Country, Brand FROM revcompare
)
SELECT
basis.Country,
basis.Brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
FROM basis
LEFT JOIN incoming On incoming.Country = basis.Country AND incoming.Brand = basis.Brand
LEFT JOIN openord On openord.Country = basis.Country AND openord.Brand = basis.Brand
LEFT JOIN revenue On revenue.Country = basis.Country AND revenue.Brand = basis.Brand
LEFT JOIN revcompare On revcompare.Country = basis.Country AND revcompare.Brand = basis.Brand
Thank you all for your help!
Since you only work with two tables, orders and rev, consider conditional aggregation by moving WHERE conditions to CASE logic for single aggregate query. Also, consider only one CTE for all possible country/brand pairs for LEFT JOIN on the two tables.
WITH cb AS (
SELECT Country, Brand FROM orders
UNION
SELECT Country, Brand FROM rev
)
SELECT cb.Country
, cb.Brand
, SUM(o.netprice) AS OrdersNet
, SUM(CASE
WHEN o.isopen = 1
THEN o.netprice
END) AS OpenOrdersNet
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.netprice
END) AS Revenue
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.rpro
END) AS RawProceeds
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.netprice
END) AS RevenueCompare
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.rpro
END) AS RawProceedsCompare
FROM cb
LEFT JOIN orders o
ON cb.Country = o.Country
AND cb.Brand = o.Brand
LEFT JOIN rev r
ON cb.Country = r.Country
AND cb.Brand = r.Brand
GROUP BY cb.Country
, cb.Brand
SQL Fiddle

SQL Combining Two Totally seperate tables to one

I am VERY new to SQL and self taught. I have two SQL that I stuggled through but got working. Now I need to combine them into one and I'm lost.
SELECT
s.lastfirst,
s.student_number,
SUM(tr.howmany)
FROM
students s
JOIN
truancies tr ON s.id = tr.studentid
WHERE
s.enroll_status = 0 AND
s.schoolid = ~(curschoolid)
GROUP BY
s.lastfirst, s.student_number
HAVING
SUM(tr.howmany) > 0
ORDER BY
s.lastfirst
And this table:
SELECT
S.DCID as DCID,
S.LASTFIRST as LASTFIRST,
S.STUDENT_NUMBER as STUDENT_NUMBER,
S2.FC_SRVC_HRS_DUE as FC_SRVC_HRS_DUE,
CASE
WHEN S2.FC_SRVC_HRS_COMPLETED IS NULL
THEN '0'
ELSE S2.FC_SRVC_HRS_COMPLETED
END AS FC_SRVC_HRS_COMPLETED,
S2.FC_SRVC_HRS_BUYOUT as FC_SRVC_HRS_BUYOUT,
CASE
WHEN S2.FC_SRVC_HRS_COMPLETED IS NULL
THEN S2.FC_SRVC_HRS_DUE * S2.FC_SRVC_HRS_BUYOUT
ELSE ((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT)
END as Balance_Due
FROM
STUDENTS S, U_STUDENTSUSERFIELDS S2
WHERE
S.DCID = S2.STUDENTSDCID AND
s.enroll_status = 0 AND
s.schoolid = ~(curschoolid) AND
(((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT) > 0 OR
((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT) IS NULL) AND
S2.FC_SRVC_HRS_DUE >.1
ORDER BY
s.lastfirst
What I am really looking for are the totals of both of these. I want the SUM(tr.howmany) from the first table and the balance due of the second BUT I need the filters that are in there. This would be sorted by student. I hope I am making sense. Any assistance would be appreciated.
You can join together 2 separate SQL select statements:
Eg:
Select A.id, A.value, B.value
From (select id, count(*) as value from TableA ...) AS A
join (select id, sum(field) as value from TableB ...) AS B
on A.id = B.id
order by A.id
As long as you have a common field to join on this would work. In your case the student_number looks like a good candidate. You will have to do the ordering outside of your subqueries.

To compute sum regarding to a constraint

I'm using PostgreSQL 8.4.
I have the following sql-query:
SELECT p.partner_id,
CASE WHEN pa.currency_id = 1 THEN SUM(amount) ELSE 0 END AS curUsdAmount,
CASE WHEN pa.currency_id = 2 THEN SUM(amount) ELSE 0 END AS curRubAmount,
CASE WHEN pa.currency_id = 3 THEN SUM(amount) ELSE 0 END AS curUahAmount
FROM public.player_account AS pa
JOIN player AS p ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id, pa.currency_id
The thing is that query does not what I expected. I realize that, but now I want to understand what exactly that query does. I mean, what SUM will be counted after the query executed. Could you clarify?
I think you have the conditions backwards in the query:
SELECT p.partner_id,
SUM(CASE WHEN pa.currency_id = 1 THEN amount ELSE 0 END) AS curUsdAmount,
SUM(CASE WHEN pa.currency_id = 2 THEN amount ELSE 0 END) AS curRubAmount,
SUM(CASE WHEN pa.currency_id = 3 THEN amount ELSE 0 END) AS curUahAmount
FROM public.player_account pa JOIN
player p
ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id;
Note that I also removed currency_id from the group by clause.
Maybe one row per (partner_id, currency_id) does the job. Faster and cleaner that way:
SELECT p.partner_id, pa.currency_id, sum(amount) AS sum_amount
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3) -- may be redundant if there are not other
GROUP BY 1, 2;
If you need 1 row per partner_id, you are actually looking for "cross-tabulation" or a "pivot table". In Postgres use crosstab() from the additional module tablefunc , which is very fast. (Also available for the outdated version 8.4):
SELECT * FROM crosstab(
'SELECT p.partner_id, pa.currency_id, sum(amount)
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3)
GROUP BY 1, 2
ORDER BY 1, 2'
,VALUES (1), (2), (3)'
) AS t (partner_id int, "curUsdAmount" numeric
, "curRubAmount" numeric
, "curUahAmount" numeric); -- guessing data types
Adapt to your actual data types.
Detailed explanation:
PostgreSQL Crosstab Query

create from select(Query error)

create table1 as SELECT from c.Name, Value, Qtr, Value as SUM(Value)
from User.Pofile a,pet_dsa_qtr_table b,User.Count c,User.Service d
WHERE Category = 'PROFESSIONAL' and Item_Name = 'PROF_PKT_RECVD' and
convert(char(32),d.User_Id) = c.Name and Service_Name_Cd = 2
and Status = 2 and d.User_Id *= a.User_Id and c.Period = b.Period
and Module = 'ACCT_TRADES' and Type in ('EQ','OPTN')
GROUP BY Name, Item_Value,
Qtr HAVING SUM(Value) >= 10000 and Item_Value in ('0', NULL);
The above was a query that was created by me as an equivalent for sybase.Can anyone help me in correcting this query
You have some quite serious syntax inconsistencies in your query which I have tried to correct (with guesswork as you haven't provided a table structure). e.g. In the select list you select "Value" but in the GROUP BY you group on "Item_Value" etc.
You also need to fully qualify your columns, some you qualify, some (like "Module" and "Type" etc) you don't.
You might also want to add a storage clause too.
Here is my best educated guess at what you are trying to achieve:
CREATE TABLE table1
AS
SELECT c.Name,
Item_Value,
Qtr,
SUM(Value) as "SUM_Value"
FROM User.Pofile a
pet_dsa_qtr_table b,
User.Count c,
User.Service d
WHERE Category = 'PROFESSIONAL'
AND Item_Name = 'PROF_PKT_RECVD'
AND TO_CHAR(d.User_Id) = c.Name
AND Service_Name_Cd = 2
AND Status = 2
AND d.User_Id = a.User_Id(+)
AND c.Period = b.Period
AND Module = 'ACCT_TRADES'
AND Type in ('EQ','OPTN')
GROUP BY c.Name,
Item_Value,
Qtr
HAVING (SUM(Value) >= 10000
AND NVL(Item_Value, '0') = '0');