I have to create an association between 2 products, which has unique product_ids and insert them into an already constructed table. The association is created based on unique part number these product ids have. For instance:
Product_id = 7578711
Part Number = 0101-2478
Product Id = 7957948
Part Number = 0101-2478
Product Id = 10558140
Part Number = 0101-2478
and my current table has the following columns:
ID (int) identity (1, 1)
product_id
date
guid
where data is in the form of:
1, 7578711, 12345, 2010-08-24 04:29:04.000,00286AFB-3880-4085-BAA0-DBCC0D59A391
I have a query which has the ability to roll Product_id to part number level and then a query to roll the part number to product_id level.
Based on the above data, where they have same part number, i want to create an association and generate insert statements which will add 2 records in the form of:
2, 7957948, 12345, 2010-08-24 04:29:04.000,00286AFB-3880-4085-BAA0-DBCC0D59A391
3, 10558140, 12345, 2010-08-24 04:29:04.000,00286AFB-3880-4085-BAA0-DBCC0D59A391
There are going to be many product IDs in that table. The above one is just an example:
I have 2 Common Table Expressions: 1 rolls the product Id to part number level, and another rolls back the part number to multiple product Ids. I am trying to avoid a cursor.
Could anyone here help me with this problem?
My 2 CTEs as as follows:
;WITH cte (product_id, item_number)
AS
(
SELECT DISTINCT --TOP 1000
pds.product_id
--,pd.productOwner_id
, i.item_number
FROM SurfwatcherEndeavorStats.dbo.productDetailBySite pds WITH ( NOLOCK )
INNER JOIN ProductData.dbo.productDimensions pd with ( NOLOCK ) ON pds.product_id = pd.product_id
INNER JOIN ProductData.dbo.options o with ( NOLOCK ) ON pds.product_id = o.product_id
INNER JOIN ProductData.dbo.items i with ( NOLOCK ) ON o.option_id = i.item_id
WHERE pds.productDetail_date > DATEADD(yyyy, -1, GETDATE())
AND i.item_number IS NOT NULL
--AND i.item_number = '0101-3258'
)
SELECT TOP 1 item_number
FROM cte WITH (NOLOCK)
WHERE product_id = 7957948
;WITH cte1 (product_id, item_number)
AS
(
SELECT DISTINCT --TOP 1000
pds.product_id
--,pd.productOwner_id
, i.item_number
FROM SurfwatcherEndeavorStats.dbo.productDetailBySite pds WITH ( NOLOCK )
INNER JOIN ProductData.dbo.productDimensions pd with ( NOLOCK ) ON pds.product_id = pd.product_id
INNER JOIN ProductData.dbo.options o with ( NOLOCK ) ON pds.product_id = o.product_id
INNER JOIN ProductData.dbo.items i with ( NOLOCK ) ON o.option_id = i.item_id
WHERE pds.productDetail_date > DATEADD(yyyy, -1, GETDATE())
AND i.item_number IS NOT NULL
)
SELECT product_id
FROM cte1 WITH (NOLOCK)
WHERE item_number = '0101-2478'
try this Sql. it should give you a complete list of all associations between two products that both use the same part number... Is that what you want ?
Select Distinct A.Product_Id, B.Product_ID
From YourTable A
Join YourTable B
On B.PartNumber = A.PartNumber
And B.Product_Id > A.Product_Id
Related
I want to get sales result for 10 days for each product which is in my orders_summary table. Currently I'm joining orders_summary table 10 times to get sales for each day. Is there any better way to get this data?
Current sql:
SELECT P.ID,
P.SKU,
FIRST_DAY.ITEMS AS ITEMS_1,
FIRST_DAY.ORDERS AS ORDERS_1,
SECOND_DAY.ITEMS AS ITEMS_2,
SECOND_DAY.ORDERS AS ORDERS_2
FROM PRODUCTS AS P
LEFT JOIN
(SELECT SKU,
AMOUNT AS ITEMS,
ARRAY_LENGTH(LIST,
1) AS ORDERS
FROM ORDERS_SUMMARY
WHERE ORDER_DATE = TO_TIMESTAMP(1633158000000 / 1000.0)) AS FIRST_DAY ON P.SKU = FIRST_DAY.SKU
LEFT JOIN
(SELECT SKU,
AMOUNT AS ITEMS,
ARRAY_LENGTH(LIST,
1) AS ORDERS
FROM ORDERS_SUMMARY
WHERE ORDER_DATE = TO_TIMESTAMP(1633676400000 / 1000.0)) AS SECOND_DAY ON P.SKU = SECOND_DAY.SKU
...
result:
select main.sku, jsonb_populate_record(null::examples.table_fields, main.json_data)
from
(
select t2.sku, jsonb_object_agg(t2.itemNames, t2.items) || jsonb_object_agg(t2.orderNames, t2.orders) as json_data from
(
select
pr.sku,
'items' || tbl_dates.num::varchar as itemNames,
coalesce(sum(sOrd.amount), 0) as items,
'orders' || tbl_dates.num::varchar as orderNames,
coalesce(sum(sOrd.qty), 0) as orders
-- tbl_dates.dates
from products pr
inner join (
select tt.num, ('2021-01-01'::date + tt.num - 1) as dates
from (
select t.num from generate_series(1, 10, 1) AS t(num)
) tt
) tbl_dates on true
left join orders_summary sOrd on sOrd.sku = pr.sku and sOrd.order_date::date = tbl_dates.dates
group by pr.sku, tbl_dates.num, tbl_dates.dates
order by tbl_dates.num
) t2
group by t2.sku
) main;
I wrote simple select query, if you want to use a function then you can change this is '2021-01-01'::date to input variable and in this code generate_series(1, 10, 1) you can change 10 to the input variable
Below query i am not able to insert common table expression select values into another table.
;WITH cte AS
(
SELECT
rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM
(SELECT
MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM
reorderHistoryDetails
GROUP BY
reorderId) rh
INNER JOIN
reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN
reorderDetails r ON r.personId = rd.personId
WHERE
rd.statusNo = 1059
)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM
cte rlst
INNER JOIN
doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN
patientdetails pd ON pd.personId = d.personId
WHERE
rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'
INSERT INTO rouletteTracking (personId, reorderStatusType, productId,
amountPaid, reorderId, orgId,
createdBy, dateCreated, timeStamp)
SELECT *
FROM cte
I'm getting this error:
Msg 208, Level 16, State 1, Line 14
Invalid object name 'cte'.
Both myself (in the comments) and Grant (in their answer) has already covered this, however, to repeat this a Common Table Expression is an expression. It's scope is limited to the scope of the statement it is define in (like other expressions). If you properly terminate your statements you'll see you have two statements above; a SELECT and an INSERT, and thus the CTE isn't defined in the second:
WITH cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM cte rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'; --This statement ends HERE
--New statement starts here. The CTE cte has no context here.
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT *
FROM cte;
Assuming you want to SELECT the TOP (5) (arbitrary) rows first from the CTE first, and then INSERT the entire result set from the CTE into your table afterwards, I would INSERT the data into a temporary table first, and then SELECT and INSERT from that:
WITH cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
SELECT *
INTO #Temp
FROM cte;
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM #Temp rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'
ORDER BY {The Column to order by}; --This statement ends HERE
--New statement starts here. The CTE cte has no context here.
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT *
FROM #Temp;
Note: I expect the INSERT statement at the end to still fail. You attempt to insert into a timestamp column (a deprecated synonym for rowversion); that isn't allowed.
The name Common Table Expression is a bit misleading. Many people read it and see the word "TABLE" very prominently. They then assume that they're dealing with a new kind of temporary table or table variable. However, the word to focus on is "EXPRESSION".
A CTE is only useable within the statement that defines it. It's not, in any way, temporary storage. It's simply a query. It's like having a sub-query to define a table like this:
SELECT *
FROM (SELECT * FROM dbo.MyTable) as NotaCTEButActsLikeOne
In order to do your process, you need to move the entire SELECT statement including your CTE into a single statement with the INSERT process.
EDIT:
Here. You just move everything so that it's a single statement:
WITH
cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM cte rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
--inner join (select min(reorderhistoryId) as reorderhistoryId,min(personId) as personId,reorderId from reorderHistoryDetails where statusNo=1059 and personId=226020 group by reorderId )as rh on d.personId=rh.personId
--inner join reorderHistoryDetails rd on rd.reorderHistoryId=rh.reorderhistoryId and rd.statusNo=1059
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06';
Also, please, the semi-colon is a statement terminator, not something you put in front of the WITH clause. Examples on line do that so that people can copy the code and it will compile (a righteous approach). However, just putting it at the end of your statements, everything will work and the code won't look awful.
I came across some weird behavior today with postgresql.
WITH actual_prices AS (
-- Looking for prices from now to the given number of days back
SELECT *
FROM prices
WHERE price_date >= now()::date - 93
)
, distinct_products_sold AS (
SELECT distinct(id_product) as pid FROM products_sold
)
, first_prices AS (
SELECT s.pid, p.product_id, p.price_date, p.price
FROM distinct_products_sold s
LEFT JOIN actual_prices p ON p.product_id = s.pid
)
select * from first_prices;
This code outputs something of this kind:
129 | | |
195 | | |
251 | | |
...
In other words, columns of table actual_prices are empty. I tried messing around with JOIN just to see what's going on: if I do RIGHT JOIN instead of LEFT JOIN, it empties the column of distinct_products_sold but the columns of actual_prices are displayed correctly. What can cause this?
You have it the wrong way around: it is not that the outer join causes data to be lost from one table, rather it forces a union between the tables by padding the missing columns with nulls e.g.
WITH P ( PID ) AS
(
SELECT *
FROM (
VALUES ( 1 ), ( 2 ), ( 3 )
) AS T ( C )
),
Q ( QID ) AS
(
SELECT *
FROM (
VALUES ( 4 ), ( 5 ), ( 6 )
) AS T ( C )
)
SELECT p.PID, q.QID
FROM P p, Q q
WHERE p.PID = q.QID
UNION
SELECT p.PID, NULL
FROM P p
WHERE p.PID NOT IN ( SELECT QID FROM Q );
Forgive me for my brainfart. Turns out it output unmatched results(how surprising). LEFT/RIGHT Joins also output unmatched results of left or right table.
P.S. Have a launch before posting a question.
No need for WITH clause here , try this:
SELECT t.pid , p.product_id, p.price_date, p.price
FROM (SELECT distinct id_product as pid FROM products_sold) t
LEFT JOIN prices p
ON(t.pid = p.product_id AND p.price_date >= now()::date - 93)
If all the columns from table prices are still NULL, then there are just no matches.
A left join keeps all the records from the leading table(the left table) and only the matched data from the right table.
I have a query with 5 columns. I want to add 1 column that defines the number of times the value in column 2 (ItemCode) occurs in the results.
This is my query:
SELECT
Items.Description_0 AS [Items.Description],
Items.ItemCode,
warehouse_location,
Stock.Quantity AS StockQty,
Stock.warehouse
FROM
((SELECT gbkmut.artcode, gbkmut.warehouse, ISNULL(gbkmut.warehouse_location,'') AS warehouse_location,
SUM(gbkmut.aantal) AS Quantity FROM gbkmut
INNER JOIN Items ON Items.ItemCode = gbkmut.artcode
INNER JOIN voorrd ON voorrd.artcode=gbkmut.artcode
AND voorrd.magcode=gbkmut.warehouse
INNER JOIN ItemUnits ON ItemUnits.Unit = Items.PackageDescription
WHERE gbkmut.reknr = Items.GLAccountDistribution
AND (( gbkmut.transtype IN ('N', 'C', 'P', 'X')
AND gbkmut.datum BETWEEN {d '2000-01-09'} AND {d '2031-02-08'} ) )
AND (gbkmut.warehouse='MAG1' )
AND Items.Type IN ('S','B')
AND NOT (Items.GLAccountAsset IS NOT NULL AND Items.IsSerialNumberItem=1) AND NOT (Items.Type = 'S' AND ItemUnits.UnitType = 'T')
GROUP BY gbkmut.artcode, gbkmut.warehouse, ISNULL(gbkmut.warehouse_location,'')
HAVING SUM(gbkmut.aantal) > 0)) Stock
INNER JOIN Items ON Items.ItemCode=Stock.artcode
WHERE Items.ItemCode like '10.27021%'
ORDER BY Items.ItemCode
Lazy version, use a common table expression (cte):
with cte as
(
[your huge select]
)
select t1.*, t2.codecount
from cte t1
join (select ItemCode, count(*) as codecount from cte group by ItemCode) as t2
ON t2.ItemCode = t1.ItemCode
Is it possible to calculate this sales report in one query? I'm not so hot with SQL and having trouble wrapping my head around joins considering the all-inclusive where clause.
TABLES (assume 1-N for foreign keys):
TABLE contacts
id
TABLE reservations
id
created_at
contact_id
TABLE reservation_trips
id
reservation_id -- never null
amount
quantity
trip_type_id -- never null
TABLE cost_adjustments
id
reservation_trip_id -- never null
cost_type_id -- never null
TABLE trip_types
id
title
TABLE cost_types
id
title
kind
EXAMPLE WHERE:
reservations.created_at >= '2013-01-01T09:00:00+00:00' AND
reservations.created_at < '2014-01-01T09:00:00+00:00' AND
reservation_trips.trip_type_id in (1, 2, 3, 4) AND
cost_adjustments.trip_type_id in (5, 6, 7, 8) AND
contact_id in (9, 10, 11, 12)
RESULT COLUMNS:
trip_types.title as trip_type_title,
COUNT(DISTINCT reservations.id) as bookings, -- distinct reservations
SUM(reservations.commissions) as commissions, -- distinct reservations
SUM(reservation_trips.id) as guests, -- distinct reservation_trips
SUM(reservation_trips.amount * reservation_trips.quantity) as gross_sales, -- distinct reservation_trips
SUM(cost_adjustments.amount * cost_adjustments.quantity) as adjustments, -- distinct cost_adjustments where cost_types.kind != 'tax'
SUM(gross_sales + adjustments - commissions) as net_sales
The table columns would look like this:
TRIP TYPE | BOOKINGS | GUESTS | GROSS SALES | ADJUSTMENTS | COMMISSIONS | NET SALES
Thanks!
The main problem is to avoid counting the intermediate levels multiple times. One way to do it is do divide by the number of items at each level.
Another approach, which can be more efficient is to put each level in it's own subquery and then join them all up.
Here's an untested stab at the first method. It might need some IsNull(xxx, 0) fixup.
Select
tt.title as trip_type_title,
count(distinct r.id) as bookings,
sum(r.commissions) / count(distinct r.id) as commissions,
count(distinct rt.id) as guests, -- this seems odd, but it's in the example
sum(rt.amount * rt.quantity) / count(distinct rt.id) as gross_sales,
sum(ca.amount * ca.quantity) as adjustments,
sum(rt.amount * rt.quantity) / count(distinct rt.id) + sum(r.commissions) / count(distinct r.id) + sum(ca.amount * ca.quantity) as net_sales
From
trip_types tt
inner Join reservation_trips rt on tt.id = rt.trip_type_id
inner Join reservations r on rt.reservation_id = r.id
left outer join cost_adjustments ca on ca.reservation_trip_id = rt.id
left outer join cost_types ct on ca.cost_type_id = ct.id
Where
r.created_at >= '2013-01-01T09:00:00+00:00' and
r.created_at < '2014-01-01T09:00:00+00:00' and
rt.trip_type_id in (1, 2, 3, 4) and
ca.trip_type_id in (5, 6, 7, 8) and
contact_id in (9, 10, 11, 12) and
ct.kind != 'tax'
Group By
tt.title
Give this a whirl, I think it covers it
SELECT trip_types.title as trip_type_title,
COUNT(DISTINCT reservations.id) as bookings,
SUM(reservations.commissions) as commissions, distinct reservations,
SUM(reservation_trips.id) as guests, distinct reservation_trips,
SUM(reservation_trips.amount * reservation_trips.quantity) as gross_sales,
SUM(cost_adjustments.amount * cost_adjustments.quantity) as adjustments, distinct cost_adjustments where cost_types.kind != 'tax'
SUM(gross_sales + adjustments - commissions) as net_sales
FROM trip_types tt
JOIN reservation_trips on tt.id = reservation_trips.trip_type_id
JOIN reservations on reservation_trips.reservation_id = reservations.id
JOIN cost_adjustments ON cost_adjustments.reservation_trip_id = reservation_trips.id
WHERE
reservations.created_at >= '2013-01-01T09:00:00+00:00' AND
reservations.created_at < '2014-01-01T09:00:00+00:00' AND
reservation_trips.trip_type_id in (1, 2, 3, 4) AND
cost_adjustments.trip_type_id in (5, 6, 7, 8) AND
contact_id in (9, 10, 11, 12)
GROUP BY trip_types.title
I conferred with a friend and figured it out. The trick that I didn't explain very well in the question (sorry) was that the search should include all trips and adjustments for reservations patching the search terms in my example where clause. So you have to use a 'with' query to first find the appropriate reservations, and then filter trips and adjustments based on that. Very tricky.
Here is the answer we found with slightly different fields actually used in production, but operate to the same effect:
with reservations_search as (
select
distinct reservations.id
from
reservations
left outer join contacts on
reservations.contact_id = contacts.id
left outer join reservation_trips on
reservations.id = reservation_trips.reservation_id
left outer join cost_adjustments on
reservation_trips.id = cost_adjustments.cost_target_id
where
#{Reservation.warez_sql(params)[0].join(' AND ')}
)
select
res_level.breeze_id,
res_level.trip_category_id,
res_level.bookings,
res_level.commissions,
res_level.guests,
adjustments_level.adjustments,
res_level.gross_sales + adjustments_level.adjustments - res_level.commissions as net_sales
FROM
(select
breeze_id,
trip_category_id,
sum(trip_commission) as commissions,
count(distinct reservation_id) as bookings,
count(reservation_trip_id) as guests,
sum(gross_sales) as gross_sales
from
-- Commissions by reservation_trips.id
(select
reservations.id as reservation_id,
reservation_trips.id as reservation_trip_id,
reservation_trips.amount * reservation_trips.quantity as gross_sales,
case
when reservations.total_cost_commissionable > 0
then coalesce(reservations.commission, 0) * reservation_trips.total_cost_commissionable / reservations.total_cost_commissionable
else 0
end as trip_commission,
trip_types.breeze_id,
trip_types.trip_category_id
from
reservations
left outer join contacts on
reservations.contact_id = contacts.id
left outer join reservation_trips on
reservations.id = reservation_trips.reservation_id
left outer join trip_types on
reservation_trips.trip_type_id = trip_types.id
where
reservations.id in (select id from reservations_search) ) as trip_level
group by
breeze_id,
trip_category_id) as res_level
-- Adjustments
left outer join
(select
trip_types.breeze_id,
sum(cost_adjustments.amount * cost_adjustments.quantity) as adjustments
from
reservation_trips
left outer join reservations on
reservation_trips.reservation_id = reservations.id
left outer join cost_adjustments on
cost_target_type = 'ReservationTrip' and
reservation_trips.id=cost_adjustments.cost_target_id
left outer join cost_types on
cost_type_id = cost_types.id
left outer join trip_types on
reservation_trips.trip_type_id=trip_types.id
where
reservations.id in (select id from reservations_search) and
cost_types.commissionable IS TRUE
group by
trip_types.breeze_id) as adjustments_level on
res_level.breeze_id = adjustments_level.breeze_id