Transact-SQL Forcing a JOIN? - sql

I'm trying to create a report with a static number of rows.
Table #1 Field #1 is a list of all Items we currently sell.
Table #2 Field #1 we have a list of all Locations that we sell from.
Table #3 contains all sales date including Item Numbers and Location Codes
I currently have a select to give me a list of all Distinct Item Numbers (Lets say 1000 of them)
Before I left Join the sales data from Table #3 I want to find a way to increase my number of rows and make a distinct row for each location for every item.
I.e. if I have a 1000 items and 10 locations I want to have 10,000 rows so that I can left join sales date ON Item Number & Location Code. I want it this way so I will have a row even if there is no sales data.
Since we havent sold every item in every location a left join from the sales table will not accomplish this. **Also the Table with Locations has no common fields with Item table to join ON. Is there a different kind of JOIN or a different SQL function to accomplish this?
Thanks!

You would use cross join and left join. For instance, to sum the sales:
select i.itemid, l.locationid, sum(t.sales)
from items i cross join
locations l left join
table3 t
on i.itemid = t.itemid and l.locationid = t.locationid
group by i.itemid, l.locationid
order by i.itemid, l.locationid;
If you only have one row in table3, then the aggregation is not necessary.

Related

SQL - Create complete matrix with all variables even if null

please provide some assistance/guidance to solve the following:
I have 1 main table which indicates sales volumes by sales person per different product type.
Where a salesperson did not sell a particular product on a particular day, there is no record.
The intention is to create null value records for salesmen that did not sell a product on a specific day. The query must be dynamic as there are many more salesmen with sales over many days.
Thanks in advance
Just generate records for all sales persons, days, and products using cross join and then bring in the existing data:
select p.salesperson, d.salesdate, st.salestype,
coalesce(t.sales_volume, 0)
from (select distinct salesperson from t) p cross join
(select distinct salesdate from t) d cross join
(select distinct salestype from t) st left join
t
on t.salesperson = p.salesperson and
t.salesdate = d.salesdate and
t.salestype = st.salestype;
Note: You may have other tables that have lists of sales people, dates, and types -- and those can be used instead of the select distinct queries.

Joining 2 tables error

I'm trying to join 2 tables to get an output report. The tables involved are the stock and dailysales table.
Stock and Dailysales tables:
Desired output format:
I am trying to join 2 tables by using the below query
Select item,article,sold,stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by item
I want the output to include all rows from the stock table. Like there is stock, but not sold, also to be included in the report. Currently my report is does not show, (stocked but not sold).
Hope you can understand my context or point me to a tutorial which I could read up. I tried to search for few days and couldn't find an exact question to mine.
Not tested -
select item,article,sum(sold),sum(stockonhand)-sum(sold) from (
select a.item,a.article,a.stockonhand,case when b.sold is null then 0 else b.sold end as sold
from stock a left join dailysales b on (a.item = b.item))
group by item,article;
It's basically does the left join and put 0 on the null column(for sum after)
and then summing up the results grouping by all the columns in the select(that what was wrong with your query)
Simply LEFT JOIN the two tables (to get the flute too), do a GROUP BY, and SUM the sold:
select s.item, s.article, coalesce(SUM(d.sold),0) as "qty sold", s.stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by s.item, s.article, s.stockonhand
The coalesce is there to replace NULL with 0, for items not sold. (Thanks sagi!)
General GROUP BY tip: If a GROUP BY clause is specified, each column reference in the SELECT list must either identify a grouping column or be the argument of a set function.
Also, you can remove the article column from the dailysales table. That data is already stored in the stock table. Never store same data twice! (Normalization!) Risk for data inconsistency if you keep that column.
You can sum the item in a separated table, it would be clearer than GROUP BY 2 tables
SELECT
s.item,
s.article,
ISNULL(ds.sold, 0) AS qtysold,
s.stockonhand
FROM
stock s OUTER APPLY
(SELECT SUM(sold) AS sold FROM dailysales WHERE item = s.item GROUP BY item) ds

Suggestion needed for this Hive Query

I have a select statement with 5 ID columns. I need to lookup and select the corresponding customer names from a Customer master table that stores Ids/names and come up with a Customer report. The tables columns are as below:
origCustomerID,Tier1PartnerID,Tier2PartnerID,DistributorId,EndCustomerID,productId,OrderTotal,OrderDate
The first 5 columns are ID columns that match CustID column in the Customers table. Note that NOT all of these columns will contain a value for a given record at all times, i.e. they could be null at times. Given the current constraints in hiveQL, I can only think of the following way, but this takes up a lot of time and is not the best possible way. Could you please suggest any improvements to this?
Select origCustomerID,a.name,Tier1PartnerID,b.name,Tier2PartnerID,
c.name,DistributorId,d.name,EndCustomerID,e.name,productId,OrderTotal,OrderDate
From Orders O
LEFT OUTER JOIN customers a on o.origCustomerID = a.custid
LEFT OUTER JOIN customers b on o.Tier1PartnerID = a.custid
LEFT OUTER JOIN customers c on o.Tier2PartnerID = a.custid
LEFT OUTER JOIN customers d on o.DistributorId = a.custid
LEFT OUTER JOIN customers e on o.EndCustomerID = a.custid
If the id values are always either customer ids or NULL (i.e. in the case they are not NULL you are sure they are customer ids and not something else) and each record in the Orders table matches at most one customer (i.e. every record has at most one id in those five columns; or possible the same id several times), you could perhaps use COALESCE in your matching expression.
I can't test this at the moment, but this should join the records using the first non-NULL id from the Orders table.
SELECT [stuff]
FROM Orders O
LEFT OUTER JOIN customers a
ON COALESCE(o.origCustomerID,
o.Tier1PartnerID,
o.Tier2PartnerID,
o.DistributorId,
o.EndCustomerID) = a.custid
Hope that helps.

Group on ID, and sum from different table (3 table total)

I have three tables,
Name - columns
Custpackingslipjour(table 1) - (deliverydate, dateclosed,, salesid)
Inventrans (Table 2) - (itemid, datephysical, transrefid)
Salesline (Table 3) - (lineamount, itemid, salesid
What i need is a single table containing all three tables, but without redundant information such as multiple of the same salesids, and with a total sum (salesline.lineamount) and where i can elminate results based on deliverydate and closed date, along with datephysical, originally i had this.
SELECT TOP (10) dbo.AX_CUSTPACKINGSLIPJOUR.DELIVERYDATE,
dbo.AX_SALESLINE.LINEAMOUNT, dbo.AX_INVENTTRANS.DATEPHYSICAL,
dbo.AX_INVENTTRANS.COSTAMOUNTPOSTED, dbo.AX_INVENTTRANS.ITEMID,
dbo.AX_INVENTTRANS.TRANSREFID, dbo.AX_CUSTPACKINGSLIPJOUR.SALESID
FROM dbo.AX_CUSTPACKINGSLIPJOUR
INNER JOIN
dbo.AX_SALESLINE ON dbo.AX_CUSTPACKINGSLIPJOUR.SALESID = dbo.AX_SALESLINE.SALESID
INNER JOIN
dbo.AX_INVENTTRANS ON dbo.AX_SALESLINE.ITEMID = dbo.AX_INVENTTRANS.ITEMID
But it produces a table with many of the same lines, because of the inherent one-to-many relationship between salesid and itemid.
So I thought i would group the sum of the value of the items associated with each salesid as that is what I am after.
Based on a few other posts on here, I tried combining the sum of lineamount from salesline by grouping it on salesid, but after 5 minutes of running, it did not appear to be a good solution, I think i have the right idea, but I am doing it wrong.
select top 10 s.SALESID,
SUM(sp.LINEAMOUNT) as 'TotalLineamount'
from AX_CUSTPACKINGSLIPJOUR as s
right outer join AX_SALESLINE as sp on s.SALESID=sp.SALESID
left outer join AX_INVENTTRANS as p on sp.ITEMID=p.ITEMID
where s.SALESID is not null
group by s.SALESID,sp.LINEAMOUNT
Any help is greatly appreciated and I will follow up any questions or comments if what I am trying to do is unclear.
Edit: Following #coltech's advice and trying with distinct on the salesid, did not work, but it was tested. I inserted the distinct(salesid) after the top 10
Edit: Following Gavins advice, i changed to
select top 10 cp.SALESID,
SUM(sl.LINEAMOUNT) as 'TotalLineamount'
from AX_CUSTPACKINGSLIPJOUR as cp
right outer join AX_SALESLINE as sl on cp.SALESID=sl.SALESID
left outer join AX_INVENTTRANS as it on sl.ITEMID=it.ITEMID
where cp.SALESID is not null
group by cp.SALESID
I also changed the prefixes for the tables to better match the table names. This works, however if I want to include more columns, for instance.
sl.LINEAMOUNT, it.DATEPHYSICAL, it.COSTAMOUNTPOSTED, it.ITEMID, it.TRANSREFID and cp.SALESID
then I will have to include these in the group by, as such.
select top 10 cp.SALESID,
SUM(sl.LINEAMOUNT) as 'TotalLineamount',
cp.DELIVERYDATE,
sl.LINEAMOUNT, it.DATEPHYSICAL,
it.COSTAMOUNTPOSTED, it.ITEMID,
it.TRANSREFID, cp.SALESID
from AX_CUSTPACKINGSLIPJOUR as cp
right outer join AX_SALESLINE as sl on cp.SALESID=sl.SALESID
left outer join AX_INVENTTRANS as it on sl.ITEMID=it.ITEMID
where cp.SALESID is not null
group by cp.SALESID, cp.DELIVERYDATE,sl.LINEAMOUNT, it.DATEPHYSICAL, it.COSTAMOUNTPOSTED, it.ITEMID, it.transrefid
However, this appears to cause the query to run for a very long time, been running for close to 25 mins. Is there a way to speed this up? Or am I just tackling this incorrectly?
I think you're fine except you shouldn't sum on the column you want to group by, so just do this instead:
select top 10 s.SALESID,
SUM(sp.LINEAMOUNT) as 'TotalLineamount'
from AX_CUSTPACKINGSLIPJOUR as s
right outer join AX_SALESLINE as sp on s.SALESID=sp.SALESID
left outer join AX_INVENTTRANS as p on sp.ITEMID=p.ITEMID
where s.SALESID is not null
group by s.SALESID

Adding a field to a view based on another row in the group

I'm looking at data for ticket sales. I need to see all the tickets in a transactions where a discount was applied to at least one ticket. Only one discount per transaction is allowed, though multiple tickets may have that discount. (For example, a AAA discount can be applied to up to 4 adult tickets, though more tickets may be purchased. I need data on all tickets purchased when the AAA Discount was used.)
End users will access the data in a view through Excel, then pivot and slice based on discount type and date. In creating the view, how can I apply the discount code from one ticket row to all tickets in the transaction?
SELECT
JnlTickets.TransNo,
JnlHeaders.FiscalDate,
JnlTickets.DiscNo AS DiscountNumber,
LEFT(JnlHeaders.agency,1) AS Agency,
Discounts.Name
FROM dbo.JnlHeaders (nolock)
LEFT JOIN dbo.JnlTickets (nolock)
ON dbo.JnlHeaders.TranNo=dbo.JnlTickets.TransNo
LEFT JOIN dbo.Discounts (nolock)
ON dbo.JnlTickets.DiscNo=dbo.DISCOUNTS.DiscountID
GROUP BY JnlTickets.TransNo,
JnlTickets.DiscNo,
JnlHeaders.FiscalDate,
LEFT(JnlHeaders.agency,1),
Jnltickets.coupons,
Discounts.Name
HAVING(JnlHeaders.FiscalDate BETWEEN '2012-03-07' AND '2012-03-08')
AND
(Jnltickets.transno IN (SELECT JnlTickets.TransNo
FROM JnlTickets
WHERE JnlTickets.DiscNo > '1'));
One of the ways to do it is to derive a table from tickets and discounts and retrieving distinct transaction number and discount id and name, and then join it back to tickets on transaction number. This will work only if you really have only one discount type per transaction.
Note: last part of having filters out all transactions where no discount was applied. This can now be achieved by changing left join of derived table to inner join.
SELECT
JnlTickets.TransNo,
JnlHeaders.FiscalDate,
TransactionsWithDiscounts.DiscNo AS DiscountNumber,
LEFT(JnlHeaders.agency,1) AS Agency,
TransactionsWithDiscounts.Name
FROM dbo.JnlHeaders (nolock)
LEFT JOIN dbo.JnlTickets (nolock)
ON dbo.JnlHeaders.TranNo=dbo.JnlTickets.TransNo
left join
(
select distinct JnlTickets.TransNo, JnlTickets.DiscNo, Discounts.Name
from dbo.JnlTickets (nolock)
LEFT JOIN dbo.Discounts (nolock)
ON dbo.JnlTickets.DiscNo = dbo.DISCOUNTS.DiscountID
where JnlTickets.DiscNo > '1' -- or is not null?
) TransactionsWithDiscounts
ON JnlTickets.TransNo = TransactionsWithDiscounts.TransNo
GROUP BY JnlTickets.TransNo,
TransactionsWithDiscounts.DiscNo,
JnlHeaders.FiscalDate,
LEFT(JnlHeaders.agency,1),
Jnltickets.coupons,
TransactionsWithDiscounts.Name
HAVING(JnlHeaders.FiscalDate BETWEEN '2012-03-07' AND '2012-03-08')
AND
(Jnltickets.transno IN (SELECT JnlTickets.TransNo
FROM JnlTickets
WHERE JnlTickets.DiscNo > '1'));