SQL Query with 4 Tables but showing multiple rows - sql

Apologies for my basic question, my minds gone blank and for some reason I can't think for the life of me what I've done.
Ok, so I have four tables
dbo.loanunits (this contains all loan units including status of in or out and loanid)
dbo.loanunits2 (this contains extra info on loan units including customer id and loanid)
dbo.loantrans (this contains transactions of loanunits, i.e. history of statuses in's and outs. also includes loanid of item sent out)
dbo.customers (this contains customer id and customer name)
This is my script:
SELECT
dbo.loanunits.loan_id, dbo.loanunits.loan_name,
dbo._loanunits.loan_status, dbo.customers.customername
FROM
dbo.loanunits
INNER JOIN
dbo.loantrans ON dbo.loanunits.loan_id = dbo.loantrans.loan_tranunitid
INNER JOIN
dbo.loanunits2 ON dbo.loantrans.loan_id = dbo.loanunits2.loan_id
INNER JOIN
dbo.customers ON dbo.loanunits2.customer_account_code = dbo.customers.customer_account_code
WHERE
(dbo.loanunits.loan_status = 'out')
I am currently getting the number of results in the transactions table but I should be getting only unique values in the loan units as I only want to see loan units out with customers and who they are with.

I do not see any use of loantrans table, so you may remove it.
SELECT
dbo.loanunits.loan_id, dbo.loanunits.loan_name,
dbo._loanunits.loan_status, dbo.customers.customername
FROM
dbo.loanunits
/*INNER JOIN
dbo.loantrans ON dbo.loanunits.loan_id = dbo.loantrans.loan_tranunitid */
INNER JOIN
/* dbo.loanunits2 ON dbo.loantrans.loan_id = dbo.loanunits2.loan_id */
dbo.loanunits2 ON dbo.loanunits.loan_id = dbo.loanunits2.loan_id
INNER JOIN
dbo.customers ON dbo.loanunits2.customer_account_code = dbo.customers.customer_account_code
WHERE
(dbo.loanunits.loan_status = 'out')
Also, you could easily use the DISTINCT keyword.

Related

On an SQL Select, how do i avoid getting 0 results if I want to query for optional data in another table?

I have a table with Customers which includes their contact person in the helpdesk. I have another table that lists all vacancies of the helpdesk employees - if they are currently sick or on vacation etc.
I need to get the helpdesk contact and the start/end time of their vacation IF there is an entry.
I currently have this (simplified):
SELECT *
FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND dbo.Projects.HDContactID = dbo.Vacations.HDContactID
So if there is a vacation listed in the Vacations table, it works fine, but if there is no vacation at all, this will not return anything - what i want is that if there is no vacation, it simply returns the other data, and ignores the missing data (returns NULL, doesn't return anything, not important)
In any case, I need to get the Customers and Project data, even if the query can't find an entry in the Vacations table. How would I do this? I pretty new to SQL and couldn't find a similar question on this site
EDIT: I'm using SQL Server, currently using HeidiSQL
Try below query:
SELECT * FROM dbo.Customers, dbo.Projects
left join dbo.Vacations on dbo.Projects.HDContactID = dbo.Vacations.HDContactID
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
Use left join as mentioned by #Flying Thunder,
Example of the left join:
SELECT country.country_name_eng, city.city_name, customer.customer_name
FROM customer
LEFT JOIN city ON customer.city_id = city.id
LEFT JOIN country ON city.country_id = country.id;
You can find a nice guide for the joins and SQL here:
https://www.sqlshack.com/learn-sql-join-multiple-tables/
You should be using LEFT JOIN. In fact, you should never be using commas in the FROM clause. That is just archaic syntax and closes the powerful world of JOINs from your queries.
I also recommend using table aliases that are abbreviations of table names. The best are abbreviations for the table names:
SELECT *
FROM dbo.Customers c LEFT JOIN
dbo.Projects p
ON c.CustomerID = p.CustomerID LEFT JOIN
dbo.Vacations v
ON p.HDContactID = v.HDContactID
WHERE c.Phone = $Phone;
Have you try this to skip vacation record if not present like this:
SELECT * FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND (dbo.Vacations.HDContactID IS NULL OR dbo.Projects.HDContactID = dbo.Vacations.HDContactID)

SQL Query Creates Duplicated Results

My task is to produce a report that shows the on time delivery of products to consumers. In essence I have achieved this. However, as you will see only some of the data is accurate.
Here is our test case: we have a sales order number '12312.' This sales order has had 5 partial shipments made (200 pieces each). The result is shown below from our DUE_DTS table.
Due Dates table data
The following code gives me the information I need (excluding due date information) to show the packing details of the 5 shipments:
DECLARE #t AS TABLE (
CUSTNAME char(35),
SONO char(10),
INVDATE date,
PACKLISTNO char(10),
PART_NO char(25),
SOBALANCE numeric(9,2)
)
INSERT INTO #t
SELECT DISTINCT c.CUSTNAME, s.SONO, p.INVDATE, p.PACKLISTNO, i.PART_NO, q.SOBALANCE
FROM [manex].[dbo].[SODETAIL]
INNER JOIN [manex].[dbo].[SOMAIN] s ON s.SONO = SODETAIL.SONO
INNER JOIN [manex].[dbo].[CUSTOMER] c ON c.CUSTNO = s.CUSTNO
INNER JOIN [manex].[dbo].[INVENTOR] i ON i.UNIQ_KEY = SODETAIL.UNIQ_KEY
INNER JOIN [manex].[dbo].[DUE_DTS] d ON d.SONO = s.SONO
INNER JOIN [manex].[dbo].[PLMAIN] p ON p.SONO = s.SONO
INNER JOIN [manex].[dbo].[PLDETAIL] q ON q.PACKLISTNO = p.PACKLISTNO
WHERE s.SONO LIKE '%12312'
SELECT * FROM #t
Here is a screenshot of the results from running this query:
Query Result
Now is when it should be time to join my due dates table (adding in the appropriate column(s) to my table definition and select statement) and make DATEDIFF comparisons to determine if shipments were on time or late. However, once I reference the due dates table, each of the 5 shipments is compared to all 5 dates in the due dates table, resulting in 25 rows. The only linking column DUE_DTS has is the SONO column. I've tried using DISTINCT and variations of the group by clause without success.
I've put enough together myself to figure joining the DUE_DTS table on SONO must be causing this to happen, as there are 5 instances of that value in the table (making it not unique) and a join should be based on a unique column. Is there a workaround for something like this?
You will need to use additional fields to join the records and reduce the results. You may need to link SONO to SODETAIL to DUE_DTS because the dates are tied to the items, not to the SONO.

Problems with updating column from another table in postgresql

My db is postgresql. I have 4 tables and need to copy id of "payment_option" record to column in "settlement" table.
Quick info about model:
Account has N invoices, Invoice has N settlements. Settlement has one of four payment options that are currently determined by column with string (PAYPAL, STRIPE, LEGACY, WIREPAYMENT). Account has N payment_options (always 4 in current state. Each type once) and each of these payment options has again one of those types in a column as string (PAYPAL, STRIPE, LEGACY, WIREPAYMENT). I need to add fkey pointing from settlement table to payment_option table that I can find settlement->invoice->account->payment_option
For this I need to do multiple join and do something like "UPDATE JOIN" but I am failing for some reason. My statement updates all settlement.payment_option_id on id of the first payment_option but not on id of payment_option bound through the account and invoice tables.
update settlement
set payment_option_id = P.id
from payment_option as P
inner join invoice as I on P.account_id = I.account_id
inner join settlement as S on S.invoice_id = I.id
where S.payment_option_type = P.type;
How to write it correctly and what is wrong? I think issue is in "SET" becuse when I write select with following joins it works as expected
Thanks Lukas
It seems strange that you are repeating the the reference to settlement in the FROM clause. Perhaps you intend:
update settlement s
set payment_option_id = P.id
from payment_option P join
invoice I
on P.account_id = I.account_id
where S.payment_option_type = P.type and S.invoice_id = I.id

Getting repeats of output, possibly doing a join wrong?

I'm having an issue where I'm getting some incorrect values in my output. I am binding the below highlighted table column with the circled column down the bottom. The service_id on the highlighted column is what is unique, but I need to bind the booking_id to retrieve the info (if that makes sense. What I end up getting is the top table where I get repeats, or the price is wrong. I should be getting only 5 lines in the top table.
Here's my code. I suspect I might be doing the join wrong?
SELECT bad.agent as Agents,
dog.SUPPLIER as SUPPLIER,
bad.status as TheStatus,
country.analysis_master1 as Country,
ftb.booking_actual_retail as BookingActualRetail,
ftb.Booking_Actual_Cost as BookingCost,
ftb.Booking_Actual_Retail_inc as BookingRetailINC,
fts.Service_Id,
fts.Service_Actual_Retail_inc as ServiceActualCostInc,
Product.SERVICE,
Product.SL_STATUS as SLSTATUS,
cat.name as Product2,
bad.LAST_SERVICE_DATE as Servicedate,
bad.LW_DATE as LWDATE,
ftb.Entered_Date as DATEENTERED,
ftb.Booking_Pax as PEOPLE,
ftb.Booking_Children as KIDS,
bad.TRAVELDATE as TRAVELDATE,
bad.FULL_REFERENCE
from BHD bad
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
inner join FTS on FTB.Booking_Id = fts.Booking_Id
inner join DRM Country on bad.agent = country.code
inner join BSL Product on bad.BHD_ID = Product.BHD_ID
inner join SRV cat on Product.SERVICE = cat.CODE
inner join OPT dog on Product.OPT_ID = dog.OPT_ID
where bad.STATUS = 'IV' AND bad.FULL_REFERENCE = 'LTIT129488'
UPDATE:
Ok, so it looks like this join here causes the multiple outputs:
inner join FTS on FTB.Booking_Id = fts.Booking_Id
I have included the two tables, their headers, and sample data
You have somewhere put the join for the service or supplier in the wrong way.. Please check this line again.
inner join SRV cat on Product.SERVICE = cat.CODE
UPDATED SOLUTION :
As per your updated screenshots, I found the issue...
you have joined like this.
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
In this join, your one table has single record against booking reference while another have multiple records against booking refrence. Thats why you are getting the multiple records in the output.
Remove this join and your problem will be solved. If you really want the data from this table then you can select in other way like using outer apply etc.

SQL Query Add to total sum if.. else dont add

How Do I achieve this result ? What I need is calculate total cost of a Product when a product is made up of components. New for me, I should add a 100$ to total cost if customer chooses for a service called Delivery.
This is what I have tried so far.
Select Sum(Component.Cost*ProductComponent.Quantity) as TotCost from ProductComponent Left Join Component on ProductComponent.ComponentId = Component.ComponentId
I Guess this will get me total cost of a product.
Now There is another table Service which has a many to many relationship with Order. Order has a many to many relationship with Service. What I need is I need to add another 100$ in total cost if there is 'deliverly' used in service.
I have attached an ER diagram of my database structure. I hope my question is clear.
The basic idea is that you'd have to put a case statement in there to add the $100 if there is a record in the Service table for a given order. The below query should get you most of the way there, but looking at the cardinality of your relationships you may need to group the results or use subqueries to chop it down to one row.
SELECT CASE WHEN sa.ServiceID IS NOT NULL THEN SUM(Component.Cost*ProductComponent.Quantity) + 100
ELSE SUM(Component.Cost*ProductComponent.Quantity) END AS TotCost
FROM ProductComponent pc
LEFT JOIN Component on ProductComponent.ComponentId = Component.ComponentId
JOIN OrderLine o ON o.ProductID = pc.ProductID
JOIN StaffAssignment sa ON sa.SaleID = o.SaleID
It looks like multiple StaffAssignments could give Service for one order so that's where you might want to use a subquery like SELECT Top 1 ServiceID FROM StaffAssignment WHERE SaleID = o.SaleID AND ServiceID IS NOT NULL
I don't have time to test this at the moment but hopefully it gives you some ideas to get this solved.
You can see the service as another product and add it to the ones already there with a UNION
SELECT TotCost = SUM(LineCost)
FROM (SELECT c.Cost * pc.Quantity as LineCost
FROM OrderLine ol
INNER JOIN ProductComponent p ON ol.ProductID = pc.ProductID
LEFT JOIN Component c on pc.ComponentId = c.ComponentId
Where ol.SaleID = #ID
UNION ALL
SELECT 100
FROM StaffAssignment sa
INNER JOIN Service s ON sa.ServiceID = s.ServiceID
Where Name = 'Delivery'
And sa.SaleID = #ID) a
Adding a field to the Services table would be useful, if the services have a flat values, to avoid to have magic constants in your code/queries