unable to join 3 tables in SQL - sql

I am lost in joining 3 tables.
I have 3 tables:
Table with Events
Table with date range
Table with reservation count for an event on a date
Now I want to create a timeline so that for every event I have a date range with for every day a count of reservations (or 0 if there are none).
See the SQL Fiddle example
Now the issue is that I can select all records from date range and events but I cannot join the reservation table together with it to select the count of reservation on that day. I need it to create a graph of last x days for an event with the reservationcount on those days.
Expected output would be that for every date (2017-01-01,2017-01-02) I have a list of events(1,2,3..) and the reservationcount on that day (or if there is no matching reservationcount record, 0)
How to do that?
2017-01-01 1 0
2017-01-02 1 65
2017-01-03 1 345
2017-01-04 1 0
2017-01-05 1 0
2017-01-06 1 0

In your sample example there is no direct link between Events and EventDate, so if you want to attach all your events to every available date you need to have a Cartesian Product (cross join) between these tables.
There may or may not be a reservation on a specific date and event, so you need a LEFT JOIN with the reservation table.
Following query should give you the desired output.
SELECT SelDate,EV.EventId, ISNULL(ResCount,0) as ResCount FROM
EventDates ED
INNER JOIN Events EV ON 1=1
LEFT JOIN Reservations RES ON ED.SelDate = RES.EventDate AND EV.EventId=RES.EventId
Or you can also write it as
SELECT ED.*, ISNULL(ResCount,0) as ResCount FROM
(
SELECT SelDate, EventId
FROM EventDates ,Events
) ED
LEFT JOIN Reservations RES ON ED.SelDate = RES.EventDate AND ED.EventId=RES.EventId

You were close, but used the wrong outer join and missed a condition.
Select EventDates.SelDate,Events.EventID
,coalesce(Reservations.ResCount,0)
from EventDates cross join Events -- one row for each event/date
left Outer Join Reservations -- join existing reservations
on Events.EventID=Reservations.EventID
and EventDates.SelDate = Reservations.EventDate
order by Events.EventID, EventDates.SelDate
See fiddle

Related

SQL Join Query with three tables

I have three tables.
1st table item_stock(item_id,item_name,item_current_qty)
2nd table item_purchase(item_id,item_purchase_qty,item_purchase_date)
3rd table item_transaction(item_id,item_txn_to,item_txn_qty,txn_date)
Item stock table has details of what on hand qty we have of all items, item_purchase table has information of which item was purchase with what quantity and item_transaction table has details of transaction happened for all items.
What I have tried is :
select item_stock.item_name,item_stock.item_current_qty as on_hand,sum(item_purchase.item_purchase_qty) as purchased ,sum(item_transaction.item_txn_qty) as Txnd
from item_stock
full outer join item_purchase on item_purchase.item_id=item_stock.item_id
full outer join item_transaction.item_id=item_stock.item_id
where item_purchase.item_purchase_date between '2022-11-01' and '2022-11-30'
and item_transaction.txn_date between '2022-11-01' and '2022-11-30'
group by item_purchase.item_id,item_transaction.item_id
order by item_stock.item_name asc
Output:
----------------------------------------------
--item_name----on_hand---purchased----Txnd----
-- TN2280 --- 25 --- 6 --- 25 ----
-- TN2590 --- 14 --- 8 --- 26 ----
----------------------------------------------
Output is showing only items which are purchased and transacted.
What I need is:
I need a table output which will display all items of item_stock with pattern like; "item_name","on_hand","purchased","Txnd". I want to put date filter in 2nd and 3rd table.
For example in November 2022 what qty was purchased for each item and how many items were transacted in the same month.
So the problem is you are using full outer joins and then including the joined tables in the where clause -- since where will be false for null items you are not getting results from a full outer join you are getting results for an inner join -- the null items are discarded.
In order to fix this you need to use left joins (probably what you did first) and then put the criteria in the join. With a left join you won't need the group by and you will just get the results you want. Like this:
SELECT item_stock.item_name,
item_stock.item_current_qty as on_hand,
sum(item_purchase.item_purchase_qty) as purchased,
sum(item_transaction.item_txn_qty) as Txnd
FROM item_stock
LEFT JOIN item_purchase ON item_purchase.item_id=item_stock.item_id
AND item_purchase.item_purchase_date between '2022-11-01' and '2022-11-30'
LEFT JOIN item_transaction ON item_transaction.item_id=item_stock.item_id
AND item_transaction.txn_date between '2022-11-01' and '2022-11-30'
GROUP BY item_purchase.item_id, item_transaction.item_id
ORDER BY item_stock.item_name ASC

Full Outer Join Not including all records from both sides

I have a calendar table with dates from 2000 through 2029. I have done a full outer join with my data table on date=tdate. My goal is to count trips occurring by date and to show a zero if there are no trips. However, all dates are not showing up. If there is no trip date in the data table, the date from the Calendar table does not show up at all. I've never seen this happen before. I've tried changing it to a left outer join which didn't work either. Actual query below.
SELECT DISTINCT c.DATE,
COALESCE(COUNT(t.TripID), 0) AS tripCount
FROM dbo.Calendar c
FULL OUTER JOIN dbo.TripsMade t ON c.DATE = CONVERT(DATE, t.tripdate, 126)
WHERE (c.DATE BETWEEN '2019-09-01' AND '2019-09-30' )
AND (t.compid = 270 OR t.compid IS NULL)
GROUP BY c.DATE
ORDER BY c.DATE
If I select all dates from the Calendar Table, they are all there. There are dates missing from the Data Table, and always will have some missing. I need all dates to show up on this report and show a zero if there are no associated records on the Data Table side.
What am I missing here?
Goal
DATE TOTAL_TRIPS
2019-09-01 3
2019-09-02 5
2019-09-03 0 <== This row is currently missing completely
2019-09-04 4
2019-09-05 0 <== This row is currently missing completely
2019-09-06 9
Your WHERE clause ends up filtering non-matches out from both tables, turning them almost into INNER JOINs (it is a little complicated with the condition on t.
I don't think you need a FULL JOIN. I suspect you want a LEFT JOIN with filtering (for the second table) in the ON clause:
SELECT c.Date, COUNT(t.TripID) AS tripCount
FROM dbo.Calendar c LEFT OUTER JOIN
dbo.TripsMade t
ON c.date = CONVERT(DATE, t.tripdate, 126) AND
t.compid = 270
WHERE c.Date BETWEEN '2019-09-01' AND '2019-09-30'
GROUP BY c.Date
ORDER BY c.Date;
If compid can actually take on NULL values and you want them, then use (t.compid = 270 OR t.compid IS NULL). I suspect, though, that is not what you want. The NULL just represents a non-match on the JOIN.
Notes:
SELECT DISTINCT is almost never used with GROUP BY, and it is not needed in this case.
COUNT() does not return NULL, so there is no need for COALESCE().
Filtering on the first table does belong in the WHERE clause.

Full outer self join with date column

I have a table contains weekly data, with a column to identify when the data was loaded in the week.
This is the sample data
====================
Load_Date |Code
====================
1 Oct 2018 |875465
8 Oct 2018 |875465
15 Oct 2018 |875465
Additionally, this table has data for all 52 weeks, so if I do a DISTINCT Load_Date, it gives me 52 rows with each week's date.
I am trying to join the table with itself such that I can identify the weeks where Code IS NULL.
The query that I tried forming is
SELECT * FROM
(--Query to get all distinct weeks
SELECT DISTINCT LOAD_DATE FROM ztable
) dates
FULL OUTER JOIN
(
SELECT DISTINCT CODE, LOAD_DATE
FROM ztable
) z
ON z.LOAD_DATE = dates.LOAD_DATE
I have tried using Left Join too but the number of records against each code is the number of rows that the code has in the table, not 52.
For example, if the code has 3 rows then I'll see only 3 rows with this join.
What am I missing?
I think you are trying to find code/load_date combinations that don't exist. Going by your description (not the sample code), I think this is:
select d.load_date, c.code
from (select distinct load_date from t) d cross join
(select distinct code from t) c left join
t
on d.load_date = t.load_date and c.code = t.code
where t.code is null;

SQL Server: query multiple tables to create a pre-formated "template" csv for export into Excel

I am trying to create the SQL necessary to accomplish the following. As an aside, I am using server side Report Builder against a hosted SQL Server database so am limited in what I can do. There are 3 tables. A salesperson table (sales), an items table (items), and a transaction table (tx).
Here is an example:
Sales Person table (sales)
Person A
Person B
Items ID table (items)
10
20
30
100
200
300
1000
2000
3000
Transaction table (tx)
100 (person A)
300 (person B)
300 (person A)
200 (person B)
Desired result in Report Builder:
Person A
Item 100: 1
Item 200: 0 (NULL)
Item 300: 1
-- NEW PAGE --
Person B:
Item 100: 0 (NULL)
Item 200: 1
Item 300: 1
My problem: here is the SQL I came up with. I need to be able to generate a consistent result set, regardless of whether an item was sold or not by a particular salesperson for easier import into Excel. In addition, I am only looking for items whose code is between 100 and 300 and within a specified date range. My SQL is ignoring the date range and item code range. I originally had these instructions in a WHERE clause but it returned only those lines that were in both tables and I lost the placeholder for any itemcode where the value was null (acting as an INNER join). Within Report Builder I will be counting how many of each items were sold by salesperson.
SELECT
tx.date, sales.salesperson, items.itemcode
FROM
tx
LEFT OUTER JOIN
itemcode ON (tx.itemcode = items.itemcode)
AND (date BETWEEN "10/1/2017" AND "12/31/2017")
AND (itemcode BETWEEN "100" AND "300")
INNER JOIN
sales ON (tx.salesID = sales.salesID)
ORDER BY
itemcode ASC
Many thanks for any and all insight into my challenge!
If you want all sales people and all items, then you can generate the rows using a cross join. You can bring in the available data using left join or exists:
select s.person, i.itemcode,
(case when exists (select 1
from tx
where tx.salesid = s.salesid and tx.itemcode = i.itemcode
)
then 1 else 0
end) as has_sold
from sales s cross join
items i
where i.itemcode between 100 and 300
order by s.saledid, i.itemcode;
If you want the count of items, use left join and group by:
select s.person, i.itemcode, count(tx.salesid) as num_sold
from sales s cross join
items i left join
tx
on tx.salesid = s.salesid and tx.itemcode = i.itemcode
where i.itemcode between 100 and 300
order by s.saledid, i.itemcode;
Here's an example that uses both cross join (to get all combinations of sales and items) and left join (to get the transactions for given dates)
SELECT
tx.date, sales.salesperson, items.itemcode
FROM
Items
CROSS JOIN
sales
LEFT OUTER JOIN
tx ON (items.itemcode = tx.itemcode)
AND date BETWEEN '10/1/2017' AND '12/31/2017'
AND (tx.salesID = sales.salesID)
WHERE
(items.itemcode BETWEEN '100' AND '300')
ORDER BY
itemcode ASC

SQL query for getting count on same table using left outer join

I have a table from which I need to get the count grouped on two columns.
The table has two columns one datetime column and another one is success value(-1,1,0)
What i am looking for is something like this:
Count of success value for each month:
month----success-----count
11------- -1 ------- 50
11------- 1 --------- 50
11------- 0 ------- 50
12------- -1 ------- 50
12------- 1 ------- 50
12------- 0 ------- 50
If there is no success value for a month then the count should be null or zero.
I have tried with left outer join as well but of no use it gives the count incorrectly.
You need to cross join all the available months, against the 3 success values to build a virtual matrix, which can then be left joined to the actual data
select m.month, s.success, COUNT(t.month)
from (select distinct MONTH from tbl) m
cross join (select -1 success union all select 1 union all select 0) s
left join tbl t on t.month = m.month and t.success = s.success
group by m.month, s.success
If you need missing months as well, that can be done, just slightly more complicated by changing the subquery "m" above.
#updated
Count(*) will always return at least 1 for left joins. count(colname) from the right part of the left join to be correct.
You probably need a table with the just the values from 1-12 to join with so you can get a zero count.