I'm working with MySQL
I have a Actions_table which has an action an number of users.
I also have a Timing_table which I map the timing of each action to do.
I can match up the action in the Actions table to the Timing table but I want it to use a default time if there is no exact match eg tables
Actions_Table
------------------------------
| Action | No Ids |
------------------------------
|Delete ID | 5 |
|Install App1 | 1 |
|Create ID | 1 |
|Rename ID | 2 |
------------------------------
Timing_Table
-------------------
|Action |Time |
-------------------
|Delete ID | 100 |
|Install App1| 200 |
|Create ID | 50 |
|Default | 60 |
--------------------
As there is nothing listed for "Rename ID" in the Timings_Table I want it to use the time value for 'Default' instead so I will have something link this.
-------------------------------------
| Action | No Ids | Total Time|
-------------------------------------
|Delete ID | 5 | 500 |
|Install App1 | 1 | 200 |
|Create ID | 1 | 50 |
|Rename ID | 2 | 120 | <== value was calculated from Default
-------------------------------------
The basic code
Select a.Action, a.`No Ids`, (a.`No Ids` * b.time) as `TotalTime
From Action_Table a, Timing_Table b
Where a.Action = b.Action
However that won't match any unmatched to Default.
What you want is a left join and a cross join:
Select a.Action, a.`No Ids`,
coalesce(b.time, def.time) as ImputedBTime,
(a.`No Ids` * coalesce(b.time, def.time)) as `TotalTime
From Action_Table a left join
Timing_Table b
on a.Action = b.Action cross join
(select t.* from Timing_Table t where t.action = 'default') def
Simple rule: Never use commas in the from clause. Always use explicit JOIN syntax. You should learn the different types of joins.
Related
I have database table like
transactions
-----------
id
code
date
amount
formalities
-----------
id
transaction_id
this is query to get max value of transactions
SELECT MAX(transaction_id) FROM `transactions` n LEFT JOIN `formalities` r ON r.transaction_id = n.id
But what I want to achieve here is to get the max value of id group by transactions code, but the value must check if the transactions have relation to formalities or not.
If yes, get max value where they relate.
If not just get the usual max value.
Is there a query to achieve something like this?
example:
transactions
--------------------
id | code | amount |
1 | ABC | 10000 |
2 | ABC | 20000 |
3 | KOO | 10000 |
4 | ABC | 20000 |
5 | KOO | 30000 |
6 | KOO | 10000 |
formalities
-----------
id | transaction_id |
1 | 3 |
2 | 5 |
The result I want is getting the following output from the transactions table
id
--
4
5 ( priority the one that have relation and get the max value)
Use a LEFT JOIN and get both - MAX(transactions.id) and MAX(formalities.transaction_id):
select t.code, max(f.transaction_id), max(t.id)
from transactions t
left join formalities f on f.transaction_id = t.id
group by t.code
The result would be
| code | max(f.transaction_id) | max(t.id) |
| ---- | --------------------- | --------- |
| ABC | NULL | 4 |
| KOO | 5 | 6 |
View on DB Fiddle
To "prioritize" transaction_id column from formalities table you can use COALESCE(), which will return the first non NULL value:
select t.code, coalesce(max(f.transaction_id), max(t.id)) as max_transaction_id
from transactions t
left join formalities f on f.transaction_id = t.id
group by t.code
Result:
| code | max_transaction_id |
| ---- | ------------------ |
| ABC | 4 |
| KOO | 5 |
View on DB Fiddle
I am not sure if I am getting the question right, but why don't you simply use -
SELECT MAX(transaction_id) FROM `transactions` n INNER JOIN `formalities` r ON r.transaction_id = n.id group by n.code
Table_Base
+----+----------------+
| ID | ACCOUNT |
+----+----------------+
| 1 | 100 |
| 2 | 120 |
| 3 | 193 |
| 4 | 201 |
| 5 | 213 |
| 6 | 247 |
| 7 | 304 |
+----+----------------+
Table_Transform
+----+----------------+
| ID | Account_Number |
+----+----------------+
| 1 | 100 |
| 2 | 9120 |
| 3 | 193 |
| 4 | 9201 |
| 5 | 9213 |
| 6 | 442 |
| 7 | 589 |
+----+----------------+
All the entries in the ACCOUNT column have multiple spaces in front of them, so I use the TRIM() function.
I need to return all ACCOUNTS in Table_Base that DO NOT appear in Account_Number in Table_Transform, while taking into account that some ACCOUNTS appear in Table_Transform with a 9 in front of them. Therefore, the ACCOUNTS that should be returned are
247
304
However, with my code, it is ignoring the AND clause and returning those that appear in Table_Transform that have the 9 in front of them. What should I fix?
SELECT * FROM Table_Base
WHERE ACCOUNT NOT IN
(SELECT Account_Number FROM Table_Transform)
AND CONCAT(9,TRIM(ACCOUNT)) NOT IN (SELECT Account_Number FROM Table_Transform);
I believe I have a problem with the CONCAT query, as I cannot even get it to work by itself in a one line SELECT statement (it will still return incorrectly).
Try this
SELECT *
FROM Table_Base
WHERE TRIM(ACCOUNT) NOT IN
(SELECT Account_Number FROM Table_Transform)
AND CAST(CONCAT('9',TRIM(ACCOUNT)) AS UNSIGNED) NOT IN (SELECT Account_Number FROM Table_Transform);
--EDIT added CAST to make extra sure --
for a direct equivalency. I would use a JOIN statement, if you're interested
I would suggest NOT EXISTS. One method is:
SELECT b.*
FROM Table_Base b
WHERE NOT EXISTS (SELECT 1
FROM Table_Transform t
WHERE t.Account_Number = trim(b.account) OR
t.Account_Number = concat(9, trim(b.account)
);
For performance reasons, though, I would split this into two expressions:
SELECT b.*
FROM Table_Base b
WHERE NOT EXISTS (SELECT 1
FROM Table_Transform t
WHERE t.Account_Number = trim(b.account)
) AND
NOT EXISTS (SELECT 1
FROM Table_Transform t
WHERE t.Account_Number = concat(9, trim(b.account))
);
This can take advantage of an index on Table_Transform(Account_Number).
I need to retrieve rows from a mysql database as follows: I have a contract table, a contract line item table, and another table called udac. I need all contracts which DO NOT have a line item record with criteria based on a relationship between contract line item and udac. If there is a better way to state this question, let me know.
Table Structures
----contract--------------------- ---contractlineitem-----------
| id | customer_id | entry_date | | id | contract_id | udac_id |
--------------------------------- ------------------------------
| 1 | 1234 | 2010-01-01 | | 1 | 1 | 5 |
| 2 | 2345 | 2016-01-31 | | 2 | 1 | 2 |
--------------------------------- | 3 | 1 | 1 |
| 4 | 2 | 4 |
| 5 | 2 | 2 |
------------------------------
---udac----------
| id | udaccode |
-----------------
| 1 | SWBL/R |
| 2 | SWBL |
| 3 | ABL/R |
| 4 | ABL |
| 5 | XRS/F |
-----------------
Given the above data, contract 2 would show up but contract 1 would not, because it has contractlineitems that point to udacs that end in /F or /R.
Here's what i have so far, but it's not correct.
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND NOT EXISTS (
SELECT cli.id
FROM contractlineitem cli_i
JOIN udac u
ON cli_i.udac_id = u.id
WHERE u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R'
AND cli_i.contract_id = cli.contract_id);
Tom's comment that your WHERE clause is wrong may be the problem you are chasing. Plus, using a correlated subquery may be problematic for performance if the optimizer can't figure out a better way to do it.
Here is the better way to do it using an OUTER JOIN:
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
LEFT OUTER JOIN udac u
ON ( u.id = cli.udac_id
AND ( u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R' ) )
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND u.id IS NULL
Try that out and see if it does what you want. The query essentially does what you stated: It tries to join to udac where the code ends in '/F' or '/R', but then it only accepts the ones where it can't find a match (u.id IS NULL).
If the same row is returned multiple times incorrectly, throw a distinct on the front.
I'm having BookTable in database (with foregin hey LibID):
| BookID | BookName | BookPrice | LibID |
-------------------------------------------
| 1 | Book_1 | 200 | 1 |
| 2 | Book_2 | 100 | 1 |
| 3 | Book_3 | 300 | 2 |
| 4 | Book_4 | 150 | 4 |
and also LibraryTable:
| LibID | LibName | LibLocation |
-----------------------------------
| 1 | Lib_1 | Loc_1 |
| 2 | Lib_2 | Loc_2 |
| 3 | Lib_3 | Loc_3 |
| 4 | Lib_4 | Loc_4 |
I need to write SQL query that will return be the info about the library and number of books for that library:
| LibID | LibName | NumberOfBooks|
------------------------------------
| 1 | Lib_1 | 2 |
| 2 | Lib_2 | 1 |
| 3 | Lib_3 | 0 |
| 4 | Lib_4 | 1 |
It should be one SQL query, probably with nested queries or joins.. Not sure how the query should look like:
SELECT L.LibID AS LibID, L.LibName AS LibName, COUNT(B) AS NumberOfBooks
FROM LibraryTable L, BookTable B
WHERE L.LibID = B.LibID
Will that work?
No, this query will not work. COUNT aggregates data, so you must explicitely tell the DBMS for which group of data you want the count. In your case this is the library (you want one result record per library).
COUNT's parameter is a column, not a table, so change this to * (i.e. count records) or a certain column (e.g. LibID).
The join syntax you are using is valid, but deprecated. Use explicit joins instead. In your case an outer join would even show libraries that have no books at all, if such is possible.
select l.libid, l.libname, count(b.libid) as numberofbooks
from librarytable l
left outer join booktable b on b.libid = l.libid
group by l.libid;
You could also do all this without a join at all and get the book count in a subquery instead. Then you wouldn't have to aggregate. That's way simpler and more readable in my opinion.
select
l.libid,
l.libname,
(select count(*) booktable b where b.libid = l.libid) as numberofbooks
from librarytable l;
SELECT lt.LibID AS LibID, lt.LibName AS LibName, count(*) AS NumberOfBooks
FROM BookTable AS bt
LEFT JOIN LibraryTable AS lt ON bt.LibID = lt.LibID
GROUP BY bt.LibID
I have table a which stores the user id and the ids of his origin and destination. On table b I have the location id and the specific name of the place. What I'm trying to do is join the tables but the name column from table b will have to be used twice since I'm trying to get 2 addresses. I'm trying to read up on MySQL but just keep doing it wrong. Any help would be appreciated.
table a
------------------------
| uid | to | from |
------------------------
| 1 | 1 | 2 |
------------------------
table b
---------------
| lid | name |
---------------
| 1 | one |
---------------
| 2 | two |
---------------
/what I'm trying to achieve/
------------------------------------------
|a.uid | a.to | b.name | a.from | b.name |
------------------------------------------
| 1 | 1 | one | 2 | two |
------------------------------------------
You will have to join table b twice, and every time using different table name (b1, b2) using as
select *
from a join b as b1 on a.to = b1.lid
join b as b2 on a.from = b2.lid
so the result would be
--------------------------------------------
|a.uid | a.to | b1.name | a.from | b2.name |
--------------------------------------------
| 1 | 1 | one | 2 | two |
--------------------------------------------
but what you probably want is to prevent name clash - if you e.g. call it from PHP - so then also rename the columns:
select a.*, b1.name as toName, b2.name as fromName
... (rest of the query as above)
If it's limited to just being twice just join in table b twice.
But it looks like you could have any number of numbers between a.from and a.to and in that case I would suggest that you do this in two or more queries.
One to get the row from a and than one to get all rows in b that is between a.from and a.to.