Select the orders placed in 1996-1997 by customers from Brazil SQL(pgAdmin4) to MongoDB - sql

--I have a collection named "orders" and in its documents I have attributes "_id",
"o_clerk", o_comment", "o_custkey" , "o_orderkey", "o_orderdate", "o_totalprice","o_orderstatus", "o_shippriority", "o_orderpriority". "o_orderdate" is is written like this: "o_orderdate" : "1993-08-24". I want to resolve this question from SQL(pgAdmin4) to MongoDB: select the orders placed in 1996-1997 by customers from Brazil. Here is the SQL query for that:
--first I extract the year from the date of placing the order (o_ordered)
SELECT DISTINCT EXTRACT (YEAR FROM o_orderdate )
FROM orders
ORDER BY 1
-- then I continued with this part
SELECT o_orderkey, c_name AS customer_name, n_name,
SUM(CASE WHEN EXTRACT (YEAR FROM o_orderdate ) = 1996 THEN o_totalprice ELSE 0 END ) AS orders_1996,
SUM(CASE WHEN EXTRACT (YEAR FROM o_orderdate ) = 1997 THEN o_totalprice ELSE 0 END ) AS orders_1997
FROM customer
INNER JOIN orders ON c_custkey=o_custkey
INNER JOIN nation ON n_nationkey=c_nationkey
WHERE n_name='BRAZIL'
GROUP BY o_orderkey, c_name, n_name
ORDER BY 1
--In MongoDB I started writing like that:
function parse_date(year) {
year = obj['o_orderdate'].getFullYear();
month = obj['o_orderdate'].getMonth() + 1;
day = obj['o_orderdate'].getUTCDate();
printjson(year);
}
db.orders.find({"_id":
ObjectId("5fb6c08fb8065357ba4fb514"),"o_orderdate":{"$gt":new
Date("1998-01-15")}},{
"o_orderdate":1}).forEach(parse_date)
--I don't know how to do this query in MongoDB.

Related

Issues with postgreSQL subqueries

I have the following chunk of code, in which in trying to count the sales of beef, chicken and pork in each month of the last year (i also need to determine the market share of the meats each month)
SELECT
CAST(EXTRACT('MONTH' FROM TO_TIMESTAMP(FULLDATE, 'YYYY-MM-DD')) AS INT) AS month
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
(
SELECT
COUNT (purchaseid) AS total_sales
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE category = 'whole milk' OR category = 'yogurt' OR category = 'domestic eggs'
GROUP BY month
) a
GROUP BY month
ORDER BY month
The expected result is the following image
EDIT to add the exact error message
but in getting this error message
syntax error at or near "SELECT"
LINE 6: SELECT
^
[SQL: SELECT
CAST(EXTRACT('MONTH' FROM TO_TIMESTAMP(FULLDATE, 'YYYY-MM-DD')) AS INT) AS month
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
(
SELECT
COUNT (purchaseid) AS total_sales
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE category = 'whole milk' OR category = 'yogurt' OR category = 'domestic eggs'
GROUP BY month
) a
GROUP BY month
ORDER BY month
This is the data schema i'm working with.
EDIT
I'm aware i can query the total_sales like this:
SELECT
CAST(EXTRACT('MONTH' FROM TO_TIMESTAMP(FULLDATE, 'YYYY-MM-DD')) AS INT) AS month,
COUNT (purchaseid) AS total_sales
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE category = 'beef' OR category = 'pork' OR category = 'chicken'
GROUP BY month
ORDER BY month
But doing it like this locks me out of doing of writting the market_share formula on the select statement because of the WHERE statement no being inside a subquery.
This query should give you the count of sales by month and category. I can't test it because I don't have datas.
SELECT
c.category,
EXTRACT('MONTH' FROM FULLDATE) AS month,
count(purchaseid) AS total_sales
FROM purchases_2020 p JOIN categories c ON p.purchaseid = c.purchase_id
WHERE category in ('beef','pork','chicken')
GROUP BY month,c.category
ORDER BY month,c.category;

Decluttering a SQL query

For a practice project i wrote the following query and i was wondering if there is way to make it more efficient than writing everything 12 times like a for loop for sql.
CREATE TABLE temp (month INT, total_sales INT, market_share decimal(5,2), year_change decimal(5,2))
insert into temp (month)
Values (1)
UPDATE temp
SET total_sales = (
SELECT COUNT(purchases_2020.purchaseid)
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31')
)
WHERE month = 1
UPDATE temp
SET market_share = (
SELECT (SELECT 100 * COUNT(purchases_2020.purchaseid)
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31'))
* 1. /
(SELECT COUNT(purchases_2020.purchaseid)
FROM purchases_2020
WHERE purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31')
)
WHERE month = 1
UPDATE temp
SET year_change = (
SELECT market_share -
(SELECT
(SELECT 100 * COUNT(purchases_2019.purchase_id)
FROM purchases_2019
JOIN categories ON purchases_2019.purchase_id = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2019.full_date BETWEEN '2019-01-01' AND '2019-01-31'))
* 1./
(SELECT COUNT(purchases_2019.purchase_id)
FROM purchases_2019
WHERE purchases_2019.full_date BETWEEN '2019-01-01' AND '2019-01-31'))
FROM temp
WHERE month = 1
)
WHERE month = 1
EDIT
I was given the 3 tables represented on the following database schema , and im trying to create a table with the total sales of dairy every month, the monthly market share of the dairy products and the difference between the 2020 monthly market share and the 2019 monthly market share (the year change colunm)
There is also an aritmethic error somewhere, when checking the project i get the following message ResultSet does not contain the correct numeric values! and im at my wits end looking for it butmy priority is to decluter the query.
Your error message tells me that you are trying to run this from a reporting tool or a host language.
It also makes no sense to put the data into separate tables by years.
SQL is a declarative language that works with data as sets.
Instead of pushing the results into table temp, try writing a query like this:
with all_data as (
select p.fulldate, p.purchaseid, c.category,
extract(year from p.fulldate) as year,
extract(month from p.fulldate) as month
from purchases_2020 p
join categories c on c.purchase_id = p.purchaseid
union all
select p.fulldate, p.purchaseid, c.category,
extract(year from p.fulldate) as year,
extract(month from p.fulldate) as month
from purchases_2019 p
join categories c on c.purchase_id = p.purchaseid
), kpis as (
select year, month,
count(purchaseid)
filter (where category in ('whole milk', 'yogurt', 'domestic eggs'))
as dairy_sales,
count(purchaseid) * 1.0 as total_sales
from all_data
group by year, month
)
select ty.month, ty.dairy_sales as total_sales,
100.0 * ty.dairy_sales / ty.total_sales as market_share,
100.0 * ( (ty.dairy_sales / ty.total_sales)
- (ly.dairy_sales / ly.total_sales)) as year_change
from kpis ty
join kpis ly
on (ly.year, ly.month) = (ty.year - 1, ty.month);

How to get the discount number of customers in prior period?

I have a requirement where I supposed to roll customer data in the prior period of 365 days.
Table:
CREATE TABLE orders (
persistent_key_str character varying,
ord_id character varying(50),
ord_submitted_date date,
item_sku_id character varying(50),
item_extended_actual_price_amt numeric(18,2)
);
Sample data:
INSERT INTO orders VALUES
('01120736182','ORD6266073' ,'2010-12-08','100856-01',39.90),
('01120736182','ORD33997609' ,'2011-11-23','100265-01',49.99),
('01120736182','ORD33997609' ,'2011-11-23','200020-01',29.99),
('01120736182','ORD33997609' ,'2011-11-23','100817-01',44.99),
('01120736182','ORD89267964' ,'2012-12-05','200251-01',79.99),
('01120736182','ORD89267964' ,'2012-12-05','200269-01',59.99),
('01011679971','ORD89332495' ,'2012-12-05','200102-01',169.99),
('01120736182','ORD89267964' ,'2012-12-05','100907-01',89.99),
('01120736182','ORD89267964' ,'2012-12-05','200840-01',129.99),
('01120736182','ORD125155068','2013-07-27','201443-01',199.99),
('01120736182','ORD167230815','2014-06-05','200141-01',59.99),
('01011679971','ORD174927624','2014-08-16','201395-01',89.99),
('01000217334','ORD92524479' ,'2012-12-20','200021-01',29.99),
('01000217334','ORD95698491' ,'2013-01-08','200021-01',19.99),
('01000217334','ORD90683621' ,'2012-12-12','200021-01',29.990),
('01000217334','ORD92524479' ,'2012-12-20','200560-01',29.99),
('01000217334','ORD145035525','2013-12-09','200972-01',49.99),
('01000217334','ORD145035525','2013-12-09','100436-01',39.99),
('01000217334','ORD90683374' ,'2012-12-12','200284-01',39.99),
('01000217334','ORD139437285','2013-11-07','201794-01',134.99),
('01000827006','W02238550001','2010-06-11','HL 101077',349.000),
('01000827006','W01738200001','2009-12-10','EL 100310 BLK',119.96),
('01000954259','P00444170001','2009-12-03','PC 100455 BRN',389.99),
('01002319116','W02242430001','2010-06-12','TR 100966',35.99),
('01002319116','W02242430002','2010-06-12','EL 100985',99.99),
('01002319116','P00532470001','2010-05-04','HO 100482',49.99);
Using the query below I am trying to get the number of distinct customers by order_submitted_date:
select
g.order_date as "Ordered",
count(distinct o.persistent_key_str) as "customers"
from
generate_series(
(select min(ord_submitted_date) from orders),
(select max(ord_submitted_date) from orders),
'1 day'
) g (order_date)
left join
orders o on o.ord_submitted_date between g.order_date - interval '364 days'
and g.order_date
WHERE extract(year from ord_submitted_date) <= 2009
group by 1
order by 1
This is the output I expected.
Ordered Customers
2009-12-03 1
2009-12-10 1
When I execute the query above I get incorrect results.
How can I make this right?
To get your expected output ("the number of distinct customers") - only days with actual orders 2009:
SELECT ord_submitted_date, count(DISTINCT persistent_key_str) AS customers
FROM orders
WHERE ord_submitted_date >= '2009-1-1'
AND ord_submitted_date < '2010-1-1'
GROUP BY 1
ORDER BY 1;
Formulate the WHERE conditions this way to make the query sargable, and input easy.
If you want one row per day (from the earliest entry up to the latest in orders) - within 2009:
SELECT ord_submitted_date AS ordered
, count(DISTINCT o.persistent_key_str) AS customers
FROM (SELECT generate_series(min(ord_submitted_date) -- single query ...
, max(ord_submitted_date) -- ... to get min / max
, '1d')::date FROM orders) g (ord_submitted_date)
LEFT join orders o USING (ord_submitted_date)
WHERE ord_submitted_date >= '2009-1-1'
AND ord_submitted_date < '2010-1-1'
GROUP BY 1
ORDER BY 1;
SQL Fiddle.
Distinct customers per year
SELECT extract(year from ord_submitted_date) AS year
, count(DISTINCT persistent_key_str) AS customers
FROM orders
GROUP BY 1
ORDER BY 1;
SQL Fiddle.

Grouping multiple selects within a SQL query

I have a table Supplier with two columns, TotalStock and Date. I'm trying to write a single query that will give me stock totals by week / month / year for a list of suppliers.
So results will look like this..
SUPPLIER WEEK MONTH YEAR
SupplierA 50 100 2000
SupplierB 60 150 2500
SupplierC 15 25 200
So far I've been playing around with multiple selects but I can't get any further than this:
SELECT Supplier,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) AS StockThisWeek,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) AS StockThisMonth,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-1-1'
GROUP BY Supplier
) AS StockThisYear
This query throws an error as each individual grouping returns multiple results. I feel that I'm close to the solution but can't work out where to go
You don't have to use subqueries to achieve what you want :
SELECT Supplier
, SUM(CASE WHEN Date >= CAST('2014-05-12' as DATE) THEN TotalStock END) AS StockThisWeek
, SUM(CASE WHEN Date >= CAST('2014-05-01' as DATE) THEN TotalStock END) AS StockThisMonth
, SUM(CASE WHEN Date >= CAST('2014-01-01' as DATE) THEN TotalStock END) AS StockThisYear
FROM StockBreakdown
GROUP BY Supplier
You may need to make the selects for the columns return only a single result. You could try this (not tested currently):
SELECT Supplier,
(
SELECT TOP 1 StockThisWeek FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisWeek
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) tmp1
WHERE tmp1.Supplier = Supplier
) AS StockThisWeek,
(
SELECT TOP 1 StockThisMonth FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisMonth
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) tmp2
WHERE tmp2.Supplier = Supplier
) AS StockThisMonth,
...
This selects the supplier and then tries to create two columns StockThisWeek and StockThisMonth by selecting the first entry from the select you created before. As through the GROUP BY there should only be one entry per supplier, so you don't lose and data.

SQL Query in CRM Report

A "Case" in CRM has a field called "Status" with four options.
I'm trying to
build a report in CRM that fills a table with every week of the year (each row is a different week), and then counts the number of cases that have each Status option (the columns would be each of the Status options).
The table would look like this
Status 1 Status 2 Status 3
Week 1 3 55 4
Week 2 5 23 5
Week 3 14 11 33
So far I have the following:
SELECT
SUM(case WHEN status = 1 then 1 else 0 end) Status1,
SUM(case WHEN status = 2 then 1 else 0 end) Status2,
SUM(case WHEN status = 3 then 1 else 0 end) Status3,
SUM(case WHEN status = 4 then 1 else 0 end) Status4,
SUM(case WHEN status = 5 then 1 else 0 end) Status5
FROM [DB].[dbo].[Contact]
Which gives me the following:
Status 1 Status 2 Status 3
2 43 53
Now I need to somehow split this into 52 rows for the past year and filter these results by date (columns in the Contact table). I'm a bit new to SQL queries and CRM - any help here would be much appreciated.
Here is a SQLFiddle with my progress and sample data: http://sqlfiddle.com/#!2/85b19/1
Sounds like you want to group by a range. The trick is to create a new field that represents each range (for you one per year) and group by that.
Since it also seems like you want an infinite range of dates, marc_s has a good summary for how to do the group by trick with dates in a generic way: SQL group by frequency within a date range
So, let's break this down:
You want to make a report that shows, for each contact, a breakdown, week by week, of the number of cases registered to that contact, which is divided into three columns, one for each StateCode.
If this is the case, then you would need to have 52 date records (or so) for each contact. For calendar like requests, it's always good to have a separate calendar table that lets you query from it. Dan Guzman has a blog entry that creates a useful calendar table which I'll use in the query.
WITH WeekNumbers AS
(
SELECT
FirstDateOfWeek,
-- order by first date of week, grouping calendar year to produce week numbers
WeekNumber = row_number() OVER (PARTITION BY CalendarYear ORDER BY FirstDateOfWeek)
FROM
master.dbo.Calendar -- created from script
GROUP BY
FirstDateOfWeek,
CalendarYear
), Calendar AS
(
SELECT
WeekNumber =
(
SELECT
WeekNumber
FROM
WeekNumbers WN
WHERE
C.FirstDateOfWeek = WN.FirstDateOfWeek
),
*
FROM
master.dbo.Calendar C
WHERE
CalendarDate BETWEEN '1/1/2012' AND getutcdate()
)
SELECT
C.FullName,
----include the below if the data is necessary
--Cl.WeekNumber,
--Cl.CalendarYear,
--Cl.FirstDateOfWeek,
--Cl.LastDateOfWeek,
'Week: ' + CAST(Cl.WeekNumber AS VARCHAR(20))
+ ', Year: ' + CAST(Cl.CalendarYear AS VARCHAR(20)) WeekNumber
FROM
CRM.dbo.Contact C
-- use a cartesian join to produce a table list
CROSS JOIN
(
SELECT
DISTINCT WeekNumber,
CalendarYear,
FirstDateOfWeek,
LastDateOfWeek
FROM
Calendar
) Cl
ORDER BY
C.FullName,
Cl.WeekNumber
This is different from the solution Ben linked to because Marc's query only returns weeks where there is a matching value, whereas you may or may not want to see even the weeks where there is no activity.
Once you have your core tables of contacts split out week by week as in the above (or altered for your specific time period), you can simply add a subquery for each StateCode to see the breakdown in columns as in the final query below.
WITH WeekNumbers AS
(
SELECT
FirstDateOfWeek,
WeekNumber = row_number() OVER (PARTITION BY CalendarYear ORDER BY FirstDateOfWeek)
FROM
master.dbo.Calendar
GROUP BY
FirstDateOfWeek,
CalendarYear
), Calendar AS
(
SELECT
WeekNumber =
(
SELECT
WeekNumber
FROM
WeekNumbers WN
WHERE
C.FirstDateOfWeek = WN.FirstDateOfWeek
),
*
FROM
master.dbo.Calendar C
WHERE
CalendarDate BETWEEN '1/1/2012' AND getutcdate()
)
SELECT
C.FullName,
--Cl.WeekNumber,
--Cl.CalendarYear,
--Cl.FirstDateOfWeek,
--Cl.LastDateOfWeek,
'Week: ' + CAST(Cl.WeekNumber AS VARCHAR(20)) +', Year: ' + CAST(Cl.CalendarYear AS VARCHAR(20)) WeekNumber,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Active'
) ActiveCases,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Resolved'
) ResolvedCases,
(
SELECT
count(*)
FROM
CRM.dbo.Incident I
INNER JOIN CRM.dbo.StringMap SM ON
I.StateCode = SM.AttributeValue
INNER JOIN
(
SELECT
DISTINCT ME.Name,
ME.ObjectTypeCode
FROM
CRM.MetadataSchema.Entity ME
) E ON
SM.ObjectTypeCode = E.ObjectTypeCode
WHERE
I.ModifiedOn >= Cl.FirstDateOfWeek
AND I.ModifiedOn < dateadd(day, 1, Cl.LastDateOfWeek)
AND E.Name = 'incident'
AND SM.AttributeName = 'statecode'
AND SM.LangId = 1033
AND I.CustomerId = C.ContactId
AND SM.Value = 'Canceled'
) CancelledCases
FROM
CRM.dbo.Contact C
CROSS JOIN
(
SELECT
DISTINCT WeekNumber,
CalendarYear,
FirstDateOfWeek,
LastDateOfWeek
FROM
Calendar
) Cl
ORDER BY
C.FullName,
Cl.WeekNumber