MySQL - Combining questions and answers from multiple transactions - sql

I have the following tables within a database, that I am using to store the results of surveys:
Each time I speak to someone at a store (read Shop), I create a new transaction, with a date/time (tblTransaction.Created). Each transaction can have many question answers associated with it.
My problem is that 2 transactions could both contain different answers to the same question and I am trying to create an SQL query/procedure to return only the latest question answers for a given store.
Here's an example:
I speak with someone at Some Shop Plc. and create a transaction on the 01/01/09 (transaction ID 1). In this transaction, I ask QuestionIDs 1 and 2. Then on the 10/01/09 I speak to them again, creating a new transaction (transaction ID 2) and answer questions 2 and 3.
I want to be able to show the list of latest answers; Question 1 from the first transaction and questions 2 and 3 from the second transaction.

SELECT
S.StoreID,
S.BranchName,
A.QuestionID,
T.Created,
A.*
FROM
tblStore S
INNER JOIN tblTransaction T ON
T.StoreID = S.StoreID
INNER JOIN tblAnswer A ON
A.TransactionID = T.TransactionID AND
A.StoreID = S.StoreID
WHERE NOT EXISTS
(
SELECT
T2.StoreID,
A2.QuestionID,
T2.TransactionID,
T2.CreatedDate
FROM
tblTransaction T2
INNER JOIN tblAnswer A2 ON
A2.TransactionID = T2.TransactionID AND
A2.StoreID = T2.StoreID
WHERE
T2.StoreID = T.StoreID AND
A2.QuestionID = A.QuestionID AND
T2.CreatedDate > T.CreatedDate
)
You can also do this by LEFT OUTER JOINing the subquery on the same criteria as in the WHERE clause and looking for a NULL value in it or you can remove the TransactionID from the subquery use a MAX() on the CreatedDate and look for a match that way with an INNER JOIN.
Keep in mind that this may not act how you are expecting if you have multiple transactions for the same store with the same created date. You may need to add an additional criteria there based on your business rules around that situation.

Related

SQLite selecting transactions that do / do not meet a particular criteria

I am trying to extract data from a GnuCash SQLite database. Relevant tables include accounts, transactions, and splits. Simplistically, accounts contain transactions which contain splits, and each split points back to an account.
The transactions need to be processed differently depending on whether each one does or does not include a particular kind of transaction fee—in this case whether or not the transaction contains a split linked to account 8190-000.
I've set up two queries, one that handles transactions with the transaction fee, and one that handles transactions without the transaction fee. The queries work, but they are awkward and wordy, and I'm sure there is a better way to do this. I did see not exists in this answer, but could not figure out how to make it work in this situation.
My current queries look like this:
-- Find all transactions containing a split with account code 8190-000
select tx_guid from transactions
inner join
(select tx_guid from
(splits inner join accounts on splits.account_guid = accounts.guid)
where accounts.code = "8190-000") fee_transactions
on fee_transactions.tx_guid = transactions.guid;
-- Find all transactions not containing a split with account code 8190-000
select guid from transactions
except
select tx_guid from transactions
inner join
(select tx_guid from
(splits inner join accounts on splits.account_guid = accounts.guid)
where accounts.code = "8190-000") fee_transactions
on fee_transactions.tx_guid = transactions.guid;
Given that I need to use these results in other queries, what is a simpler and more succinct way to obtain these lists of transactions?
You can use EXISTS for your 1st query like this:
SELECT t.*
FROM transactions t
WHERE EXISTS (
SELECT 1
FROM splits s INNER JOIN accounts a
ON s.account_guid = a.guid
WHERE a.code = '8190-000' AND ?.tx_guid = t.guid
);
Change ? to s or a, depending on which table contains the column tx_guid (splits or accounts), since it is not clear in your question.
Also, change to NOT EXISTS for your 2nd query.

How to join 4 tables in SQL?

I just started using SQL and I need some help. I have 4 tables in a database. All four are connected with each other. I need to find the amount of unique transactions but can't seem to find it.
Transactions
transaction_id pk
name
Partyinvolved
transaction.id pk
partyinvolved.id
type (buyer, seller)
PartyCompany
partyinvolved.id
Partycompany.id
Companies
PartyCompany.id pk
sector
pk = primary key
The transaction is unique if the conditions are met.
I only need a certain sector out of Companies, this is condition1. Condition2 is a condition inside table Partyinvolved but we first need to execute condition1. I know the conditions but do not know where to put them.
SELECT *
FROM group
INNER JOIN groupB ON groupB.group_id = group.id
INNER JOIN companies ON companies.id = groupB.company_id
WHERE condition1 AND condition2 ;
I want to output the amount of unique transactions with the name.
It is a bit unclear what you are asking as your table definitions look like your hinting at column meanings more than names such as partycompany.id you are probably meaning the column that stores the relationship to PartyCompany column Id......
Anyway, If I follow that logic and I look at your questions about wanting to know where to limit the recordsets during the join. You could do it in Where clause because you are using an Inner Join and it wont mess you your results, but the same would not be true if you were to use an outer join. Plus for optimization it is typically best to add the limiter to the ON condition of the join.
I am also a bit lost as to what exactly you want e.g. a count of transactions or the actual transactions associated with a particular sector for instance. Anyway, either should be able to be derived from a basic query structure like:
SELECT
t.*
FROM
Companies co
INNER JOIN PartyCompancy pco
ON co.PartyCompanyId = pco.PartyCompanyId
INNER JOIN PartyInvolved pinv
ON pco.PartyInvolvedId = pinv.PartyInvolvedId
AND pinv.[type] = 'buyer'
INNER JOIN Transactions t
ON ping.TransactionId = t.TransactionId
WHERE
co.sector = 'some sector'

SQL: If there is No match on condition (row in table) return a default value and Rows which match from different tables

I have three tables: Clinic, Stock and StockLog.
I need to get all rows where Stock.stock < 5. I need to also show if an order has been placed and what amount it is; which is found in the table Stocklog.
The issue is that a user can set his stock level in the Stock table without placing an order which would go to Stocklog.
I need a query that can : return the rows in the Stock table and get the related order amounts in the Stocklog table. If no order has been placed in StockLog, then set amount to order amount to zero.
I have tried :
SELECT
Clinic.Name,
Stock.NameOfMedication, Stock.Stock,
StockLog.OrderAmount
FROM
Clinic
JOIN
Stock ON Stock.ClinicID = Clinic.ClinicID
JOIN
StockLog ON StockLog.StockID = Stock.StockID
WHERE
Stock.Stock <= 5
The issue with my query is that I lose rows which are not found in StockLog.
Any help on how to write this.
Thank you.
I am thinking the query should look like this:
SELECT c.Name, s.NameOfMedication, s.Stock,
COALESCE(sl.OrderAmount, 0) as OrderAmount
FROM Stock s LEFT JOIN
Clinic c
ON s.ClinicID = c.ClinicID LEFT JOIN
StockLog sl
ON sl.StockID = s.StockID
WHERE s.Stock <= 5 ;
You want to keep all rows in Stock (subject to the WHERE condition). So think: "make Stock the first table in the FROM and use LEFT JOIN for all the other tables."
If you want to keep all the rows that result from joining Clinic and Stock, then use a LEFT OUTER JOIN with StockLog. I don't know which SQL you're using (SQL Server, MySQL, PostgreSQL, Oracle), so I can't give you a precise example, but searching for "left outer join" in the relevant documentation should work.
See this Stack Overflow post for an explanation of the various kinds of joins.

Joining Three tables at the same time? [duplicate]

This question already has answers here:
SQL JOIN and different types of JOINs
(6 answers)
Closed 7 years ago.
I have a table with multiple transaction types. Currently I'm only pulling the adjustments and payments, but I am needing to match the IDs of the types to their descriptions. For example, my transactions table has paytype_id and adjustment_id which range 1-100 for each. I have two other tables dbo.paytype and dbo.adjustments that have the distinct paytype_id and adjustment_id along with the pay_desc and adj_desc fields that describe what kind they are. Issue I'm running into is that any given transaction will only have a paytype_id or an adjustment_id but not both. So if I attempt to join one table, then the other I lose the NULL values of the secondary IDs
This query will pull the paytype_id descriptions but will remove any transactions with adjustment_id due to the payment_id being NULL for them.
SELECT
t.tran_num, t.resp_party_id, t.Total,
t.paytype_id, t.adjustment_id, t.clinic,
t.date_entered, p.pay_desc
FROM
adjpay_vw t
CROSS JOIN
paytype p
WHERE
(t.paytype_id = p.paytype_id AND t.clinic = p.clinic)
So I'm wondering how I can pull both the adj_desc from dbo.adjustments and the pay_desc from dbo.paytype
By having your join condition in the WHERE, it forces it to behave like an INNER JOIN. Instead of using the WHERE, add it to the JOIN condition and flip to a LEFT JOIN.
SELECT t.tran_num, t.resp_party_id, t.Total, t.paytype_id, t.adjustment_id, t.clinic, t.date_entered, p.pay_desc, adj.adj_desc, ISNULL(p.pay_desc,adj.adj_desc) [Description]
FROM adjpay_vw t
LEFT JOIN paytype p ON t.paytype_id=p.paytype_id AND t.clinic=p.clinic
LEFT JOIN dbo.adjustments adj ON adj.adjustment_id = t.adjustment_id
Also, since I wasn't sure your view contained the adjustment description, I've added a join for that and added both descriptions and a new columns that shows the description no matter if it was an adjustment or payment.

Uses of unequal joins

Of all the thousands of queries I've written, I can probably count on one hand the number of times I've used a non-equijoin. e.g.:
SELECT * FROM tbl1 INNER JOIN tbl2 ON tbl1.date > tbl2.date
And most of those instances were probably better solved using another method. Are there any good/clever real-world uses for non-equijoins that you've come across?
Bitmasks come to mind. In one of my jobs, we had permissions for a particular user or group on an "object" (usually corresponding to a form or class in the code) stored in the database. Rather than including a row or column for each particular permission (read, write, read others, write others, etc.), we would typically assign a bit value to each one. From there, we could then join using bitwise operators to get objects with a particular permission.
How about for checking for overlaps?
select ...
from employee_assignments ea1
, employee_assignments ea2
where ea1.emp_id = ea2.emp_id
and ea1.end_date >= ea2.start_date
and ea1.start_date <= ea1.start_date
Whole-day inetervals in date_time fields:
date_time_field >= begin_date and date_time_field < end_date_plus_1
Just found another interesting use of an unequal join on the MCTS 70-433 (SQL Server 2008 Database Development) Training Kit book. Verbatim below.
By combining derived tables with unequal joins, you can calculate a variety of cumulative aggregates. The following query returns a running aggregate of orders for each salesperson (my note - with reference to the ubiquitous AdventureWorks sample db):
select
SH3.SalesPersonID,
SH3.OrderDate,
SH3.DailyTotal,
SUM(SH4.DailyTotal) RunningTotal
from
(select SH1.SalesPersonID, SH1.OrderDate, SUM(SH1.TotalDue) DailyTotal
from Sales.SalesOrderHeader SH1
where SH1.SalesPersonID IS NOT NULL
group by SH1.SalesPersonID, SH1.OrderDate) SH3
join
(select SH1.SalesPersonID, SH1.OrderDate, SUM(SH1.TotalDue) DailyTotal
from Sales.SalesOrderHeader SH1
where SH1.SalesPersonID IS NOT NULL
group by SH1.SalesPersonID, SH1.OrderDate) SH4
on SH3.SalesPersonID = SH4.SalesPersonID AND SH3.OrderDate >= SH4.OrderDate
group by SH3.SalesPersonID, SH3.OrderDate, SH3.DailyTotal
order by SH3.SalesPersonID, SH3.OrderDate
The derived tables are used to combine all orders for salespeople who have more than one order on a single day. The join on SalesPersonID ensures that you are accumulating rows for only a single salesperson. The unequal join allows the aggregate to consider only the rows for a salesperson where the order date is earlier than the order date currently being considered within the result set.
In this particular example, the unequal join is creating a "sliding window" kind of sum on the daily total column in SH4.
Dublicates;
SELECT
*
FROM
table a, (
SELECT
id,
min(rowid)
FROM
table
GROUP BY
id
) b
WHERE
a.id = b.id
and a.rowid > b.rowid;
If you wanted to get all of the products to offer to a customer and don't want to offer them products that they already have:
SELECT
C.customer_id,
P.product_id
FROM
Customers C
INNER JOIN Products P ON
P.product_id NOT IN
(
SELECT
O.product_id
FROM
Orders O
WHERE
O.customer_id = C.customer_id
)
Most often though, when I use a non-equijoin it's because I'm doing some kind of manual fix to data. For example, the business tells me that a person in a user table should be given all access roles that they don't already have, etc.
If you want to do a dirty join of two not really related tables, you can join with a <>.
For example, you could have a Product table and a Customer table. Hypothetically, if you want to show a list of every product with every customer, you could do somthing like this:
SELECT *
FROM Product p
JOIN Customer c on p.SKU <> c.SSN
It can be useful. Be careful, though, because it can create ginormous result sets.