How to write the Join Query? - ms-access

I am having trouble with a join I need to do -
Here's what the problem is:
I have a database of 17 salespeople, each salesperson has his own totals such as expenses and advances and commissions.
In my database there are about 200 customers that can have up to 3 salespeople on them. That is the minority but it does exist. Each salesperson is paid on invoices for that account. Some of my salespeople are only the 2nd salesperson on an account, meaning they are never the 1st, so if I do a join of a salesperson to invoices - he doesn't have any invoices because he is never salesman 1.
I can pull invoices for him as salesman 2 when I am only looking at invoices, but every 8 weeks I need to join his expenses where he is salesman 1 to his invoices where he is salesman 2. I cannot get it to work or come up with a solution.
I have a salesperson query so every 8 weeks I can call salesman number 100 to see his expenses
I have a invoices/payments query and see what he has in for payments
- but my 3rd query where I am trying to bring the salesman together with his payments doesn't work because he is the 1st salesman in expenses and the 2nd salesman in the invoices/payments
I have tried every which way in SQL and can't seem to get it right.
Is there a way I can join the salesman1 from the salesman query to the invoices/payments query on slmn1 or slmn2 or slmn3?
this example is too complex:
FROM qryFinalWeek
INNER JOIN QryFW ON qryFinalWeek.SLMN = QryFW.SLMN2 OR QryFW.SLMN3 OR QryFW.Salesrep1
WHERE (((QryFW.PDATE)=[FDATE]+4));
this example is only giving invoices where he's first salesman so i can't get any commissions if he is the second:
WHERE (((qryFinalWeek.SLMN)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4)) OR (((QryFW.SLMN2)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4)) OR (((QryFW.SLMN3)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4))
any feedback is appreciated!

The data structure has created the problem for you. My suggestion would be to remove the sales people from your Customers table (assuming that there is more data per customer than just the associated sales people) and add them to a new table called Salesmen_Customers or something like that. This table would contain a many to many relationship between Salesmen and Customers
Here is your current structure
And here is the new structure
That should simplify the query structure significantly. This will also scale easily if your sales staff ever goes more that 3 deep. If you are interested is further reading on the topic of database design I'd recommend SQL anti patterns. If you are really married to you DB structure you can query your way out, but I wouldn't recommend it.

Related

How to use joins to fetch unique data from a single table in mysql?

I've 4 tables in the database which are:-
branch
deposit
borrow
customers
Query: Find loan no, loan city, deposit account number, deposit city of those customers who are living in Nagpur city?
I've written a query below which successfully fetches three data.
Loan No
Loan City
Deposit Account number
But while fetching Deposit city, I'm getting same data as of Loan city.
Below I'm sharing my code so that you can find where I'm wrong
My tried code:
SELECT borrow.loanno,
branch.city AS 'Borrow city',
deposit.actno,
branch.city AS 'Deposit city',
customer.city As 'Customer_city'
FROM customer
JOIN borrow
ON borrow.cname = customer.cname
JOIN branch
ON borrow.bname = branch.bname
JOIN deposit
ON deposit.cname = customer.cname
AND customer.city = 'Nagpur';
By running the above code, Output I get is
loanno Borrow city Deposit city actno Customer_city
321 Mumbai Mumbai 104 Nagpur
375 Mumbai Mumbai 105 Nagpur
Output I want is:-
loanno Borrow city Deposit city actno Customer_city
321 Mumbai Delhi 104 Nagpur
375 Mumbai Banglore 105 Nagpur
For more understanding I'm sharing my dB fiddle link so that you get a clear idea.
https://www.db-fiddle.com/f/gaUYxuwuJLsWA4kFeMn9u6/6
Please dont take this the wrong way, but what you have does not appear to be a good database design. Typically, each table would have an auto-increment integer-based ID key that is unique and carried through to each underlying table. You have string values for your primary keys.
I know this looks like the beginning of a database design, but you really need some assistance moving forward.
So lets say, you have 5 people named "Anil", what do you do then... your done. "Anil" is the primary key and you cant change it. By having an auto-increment, you could have 1000 Anil customers, each with additional information to ensure uniqueness... first name, last name, address, other unique info.
Now on to your branch lookup table. Same applies. you have multiple branches in the city of Mumbai. WOULD there ever be a duplicate name? Possible??? Hence having an ID column auto-increment would resolve, and additional info like customer would ensure uniqueness like the address. Such as the "Kranti" branch. "IF" you had a Kranti branch on X-street vs Kranti branch on Y-street you would be safe with unique auto-increment numbers.
Now on to deposit and borrow tables. I know its sample data, but just to show how an oops can happen. Your branch info has a name "Chandani", but your deposit table has "Chandni" (missing the second "a"). I know type-o.
When you have your data entry screens and you find a customer or a branch, the system will have the given "id" of the record to store in your given deposit or borrow table. Don't do based on strings per the customer example of Anil -- which one if you have 100 Anil customers. The ID will ensure the correct customer. Similar with branch. The ID columns for customer and branch should be used instead (based on a modified database/table structures).
Finally to your query. You are trying to join the borrow table with the deposit for a given person. This is just going to give you headaches. There is no guarantee of a deposit for a borrow (or vice-versa). Now, if you have 5 borrows and 7 deposits for Anil, and you try to query both the way you have it, you will get 35 records being returned (a Cartesian product) because for every record in the borrow table with matching customer will be applied to those deposits.
So, enough for you to consider, sit back, take a breath and digest it. I can show you a better table structure to handle some context presented here.
As for writing queries, you really need to present it in simple English what you are TRYING to get, then show how you are ATTEMPTING. Also, there appears to be no match such as a deposit being applied to a given borrow, so what's the point / logic on this.
Again, sincere on what you are up against and would like to help but you may be farther off than you realize.
FEEDBACK
Ok, so it is an assignment -- a bad one, but one none-the-less. So the issue is you only used one instance of the branch based on the borrower. What you need is the branch table joined TWICE... once to the borrower, once for the deposit. In each, I "aliased" it. When joining to the borrow table I aliased it as "BBranch" for "borrow branch". For the deposit, I aliased as "DBranch" for "deposit branch". So each one is joined respectively, so each one will point to its own and pull the city respectively.
select
borrow.loanno,
BBranch.city as 'Borrow city',
deposit.actno,
DBranch.city as 'Deposit city',
customer.city
from
customer
join borrow
on customer.cname = borrow.cname
join Branch BBranch
on borrow.bname = BBranch.bname
join deposit
on customer.cname = deposit.cname
and customer.city = 'Nagpur'
join branch DBranch
on deposit.bname = DBranch.bname
Also, indentation to see how / where the tables are joined shows. You can see it goes
customer
borrow
branch for borrow
deposit
branch for deposit
Then you can always see the relationships down stream and not overlapping others / criss-crossing.
The last item is the and customer.city = 'Nagpur'. Don't even know if that was a requirement, but it was in your query.

Sum on multi-joined tables

I have 4 tables
customers
sales
sale_items
stock_items
I want to list all my customers. For each customer I want the total amount purchased - a field that I want the sql query to create (xxx as totalSales)
I tried selecting customers as the primary table and joining other tables on it. I tried selecting sales as the primary table and also the sale_items. I kind of got the correct sum calculation but it will show me the first customer first.
This is what my tables look like:
*TABLE customers*
id
customer_name
email
*TABLE sales*
id
customer_id
transaction_date
*TABLE sale_items*
id
sale_id
stock_item_id
*TABLE stock_items*
id
item_name
price
I want to
Create a list of all customers, sorted by the customer with the most sales (in value) first. Not the count of the sales, but the total amount (sales value) of the sales
Display the items purchased per customer under the customer name. This would not be per order, but for all sales. So if a tin of coffee was purchased by customer X, over a count of 4 orders each, the tin of coffee would display 4 times. Though if possible I'd like the items listed a-z.
I have inner joined the tables on each other, so I would get a list of all transactions. I've tried SELECT * FROM customers, tried FROM sales, tried from sale_items. I've tried GROUP_BY customer_id, but then I would get incorrect counts.
I want to display the data as such.
CUSTOMER ITEMS TOTAL VALUE
John Doe Coffee
Milk
Tea
Milk
Bread
Coffee 500
Jane Doe Coffee
Milk
Coke 350
Denver Doe Coffee
Milk
Bread
Bread 125
I don't want to use PHP and make a query for every single "thing". I am trying to run one or two queries.
I don't think you're going to be able to quite get them in separate rows like you're showing (unless you look into the ROLLUP function), but that's probably not a requirement for you.
I think you should use GROUP_CONCAT which is an aggregation function sort of like SUM but it creates a comma separated list out of all of the values:
SELECT
*
SUM(sale_amount) as total_sales,
GROUP_CONCAT(item_name) as item_names
FROM customers c
JOIN sales s USING (customer_id)
JOIN sale_items si USING (sale_id)
JOIN stock_items sit USING (stock_item_id)
GROUP BY customer_id
What you should see as a sample row is:
Denver Doe Coffee,Milk,Bread,Bread 125
(I had to make up some column names, like sale_amount, but you must have those in there I'm sure. You'll have to make some adjustments in those names and maybe in how I did the joins as well, but this should work with some changes if I'm understanding your needs).

Client Management DB Design - Track credit based purchases

My reservation system allows us to purchase credits for clients in terms of pre defined packages. I'm struggling with how I record and calculate available credits.
Let's say we're talking about a car wash service. A client can have multiple cars and can purchase the following services, 'Wash and Wax' and 'Detailing'.
Client 1 has two cars, Car A and Car B. He brings them both in and purchases:
Car A - 1 Wash and Wax
Car A - 1 Detailing
Car B - 10 Wash and Wax
Car B - 1 Detailing
This generates 4 rows in my Purchases table, one for each service purchased.
In my DB I have two related tables tracking purchases and reservations. Table 1 Purchases, Table 2 Reservations.
In Purchases I have the following fields of note:
id
client_id
car_id
service_id
credits_purchased
credits_scheduled
credits_used
cart_id
Then in my Reservation table I have the following fields of note:
id
client_id
car_id
service_id
reservation_date
completed_datetime
car_in_datetime
car_out_datetime
purchase_id
I track the credits available by updating the Purchases table fields credits_used and credits_on_schedule as events happen.
For example, when the client makes a reservation the system adds a new record in the Reservations table, once this happens the system also runs an update query and adds +1 to the related Purchases table credits_on_schedule. When the Reservation is updated to complete the system also updates the Purchases table and adds -1 to credits_on_schedule and +1 to credits used. Simple math between credits_purchased, credits_used, and credits_on_schedule derive what credits are available for a client to use.
I feel like this isn't a good way to track the credits. My question is what is a better implementation? Should I just track credits_purchased then use count queries on the Reservation table to calculate credits_used and credits_on_schedule? Should I be using a pivot table to track? I can't seem to wrap my head around what is the cleanest design.
It looks to me that the design is ok in general.
A reservation can only have one purchased related to it so purchase_id field is a foreign key in Reservation table.
Nevertheless, my advise to you is to create a log system of all these record updates.
As you mentioned, as events are fired the system updates the calculated fields.
What if for some reason the system fails at a certain point? You should be able to track these events.
One way to avoid this is, as you mentioned, calculate credit_used by a count query on all completed reservations.

Relational Database Logic

I'm fairly new to php / mysql programming and I'm having a hard time figuring out the logic for a relational database that I'm trying to build. Here's the problem:
I have different leaders who will be in charge of a store anytime between 9am and 9pm.
A customer who has visited the store can rate their experience on a scale of 1 to 5.
I'm building a site that will allow me to store the shifts that a leader worked as seen below.
When I hit submit, the site would take the data leaderName:"George", shiftTimeArray: 11am, 1pm, 6pm (from the example in the picture) and the shiftDate and send them to an SQL database.
Later, I want to be able to get the average score for a person by sending a query to mysql, retrieving all of the scores that that leader received and averaging them together. I know the code to build the forms and to perform the search. However, I'm having a hard time coming up with the logic for the tables that will relate the data. Currently, I have a mysql table called responses that contains the following fields,
leader_id
shift_date // contains the date that the leader worked
shift_time // contains the time that the leader worked
visit_date // contains the date that the survey/score was given
visit_time // contains the time that the survey/score was given
score // contains the actual score of the survey (1-5)
I enter the shifts that the leader works at the beginning of the week and then enter the survey scores in as they come in during the week.
So Here's the Question: What mysql tables and fields should I create to relate this data so that I can query a leader's name and get the average score from all of their surveys?
You want tables like:
Leader (leader_id, name, etc)
Shift (leader_id, shift_date, shift_time)
SurveyResult (visit_date, visit_time, score)
Note: omitted the surrogate primary keys for Shift and SurveyResult that I would probably include.
To query you join shifts and surveys group on leader and taking the average then jon that back to leader for a name.
The query might be something like (but I haven;t actually built it in MySQL to verify syntax)
SELECT name
,AverageScore
FROM Leader a
INNER JOIN (
SELECT leader_id
, AVG(score) AverageScore
FROM Shift
INNER JOIN
SurveyResult ON shift_date = visit_date
AND shift_time = visit_time --depends on how you are recording time what this really needs to be
GROUP BY leader ID
) b ON a.leader_id = b.leader_id
I would do the following structure:
leaders
id
name
leaders_timetabke (can be multiple per leader)
id,
leader_id
shift_datetime (I assume it stores date and hour here, minutes and seconds are always 0
survey_scores
id,
visit_datetime
score
SELECT l.id, l.name, AVG(s.score) FROM leaders l
INNER JOIN leaders_timetable lt ON lt.leader_id = l.id
INNER JOIN survey_scores s ON lt.shift_datetime=DATE_FORMAT('Y-m-d H:00:00', s.visit_datetime)
GROUP BY l.id
DATE_FORMAT here helps to cut hours and minutes from visit_datetime so that it could be matched against shift_datetime. This is MYSQL function, so if you use something else you'll need to use different function
Say you have a 'leader' who has 5 survey rows with scores 1, 2, 3, 4 and 5.
if you select all surveys from this leader, sum the survey scores and divide them by 5 (the total amount of surveys that this leader has). You will have the average, in this case 3.
(1 + 2 + 3 + 4 + 5) / 5 = 3
You wouldn't need to create any more tables or fields, you have what you need.

ssis duplicate rows and script componenet

So, not sure how to explain this but here goes. I am trying to translate a table of commissions earned. Some of the records have the same invoice and item meaning multiple people earned commissions on that product. I want to merge those records so I only have one record per product per invoice. Basically, like a pyramid scheme, salespeople make commissions on sales and depending on what level they are at, (1,2,3,4) all salespeople above them get a commission as well. So I need to find records where invoice and item are the same,
pull data from each of those records and then output only one record with that data.
I am pretty sure a script component is the way to go but I am not sure how to pull multiple rows of data in to check for duplicate invoice and item, also keeping some of the data for each row to output together.
Let me know if you have any questions, I know I didn't word that the best.
The source table looks like this
COMMISSION Table
SalespersonID - the person getting the commission
InvoiceID - ID of invoice
ItemID - ID of specific lineitem on invoice
ProductID - ID of product sold
Sales_Amt - total for that lineitem
Salesperson Downlevel ID - ID of salesperson below SalespersonID if exists
Commission Rate - percentage Salesperson receives
Commission Total - amt salesperson receives
There are some other columns but these are the important ones.
I am translating this into a table that looks like this
New COMMISSION Table
CommissionId - Primary key for commission table
CommissionPayable - amt salesperson gets
CommissionPayableUpline1 - amt salespersonUpline1 gets
CommissionPayableUpline2 - amt salespersonUpline2 gets
CommissionPayableUpline3 - amt salespersonUpline3 gets
CommissionRate - percent salesperson gets of amount
CommissionRateUpline1 - percent salesperson upline1 gets
CommissionRateUpline2 - percent salesperson upline2 gets
CommissionRateUpline3 - percent salesperson upline3 gets
SalespersonId - Id of salesperson
SalespersonUpline1 - ID of salesperson upline1
SalespersonUpline2 - ID of salesperson upline2
salespersonUpline3 - ID of salesperson upline3
So you see we can have up to four levels of salespeople. Most of our salespeople are only 1 to 2 levels but we do have a few 3 levels deep and none currently that are four levels deep. The way it is stored in the old database is confusing and not very efficient.
I want to find rows with duplicate invoice and item ids, grab the salespeople, and down level salespeople as well as commission rates and amounts for each, then insert all that into one row in new system.
Well I finally got this figured out by using the ssis script component and then doing some fancy sorting and creating new records on output.