I have a DB table
frequency table as follows
(companyid, task, and frequency) is unique key
| companyid| task |frequency | date |
| -------- | ----- | ------- | ----- |
| 123 | abc | Day | 03-03-2020 |
| 123 | def | Week | 09-03-2020 |
| 456 | abc | Week | 10-03-2020 |
| 456 | klm | Week | 12-03-2020 |
History table as follows PRIMARY KEY ("companyid", "RECORDNO")
| companyid| RECORDNO | STAGE | STATE |
| -------- | ----- | ------- | ----- |
| 123 | 12 | 1 | P |
| 456 | 13 | 2 | A |
| 123 | 13 | 1 | S |
Entry table , unique ("companyid", "ID")
| companyid| ID | STATE | task |
| -------- | ----- | ----- | ----- |
| 123 | 12 | Purchased | abc |
| 123 | 13 | Accesible | def |
| 456 | 13 | Store | abc |
| 456 | 14 | Store | klm |
I am running a job that frequently runs every 6 hours.
I have to fetch unique companyids where the state in history table is 'S' and has the task in frequency = task in entry table and recordno is history table = id in entry table and current system date is greater than the date in the frequency table.
The basic need is to process the job(entries from entry table) which has less date than the current system date. (Note the system date is always in UTC and date in frequency table can be configured by the user).
companyid is common in all tables.
I have tried the below query to fetch the result without having the date filters.
Need help in getting the time filter properly.
select distinct cny#
from frequency
where exists
(select 1
from history, entry
where history.companyid = entry.companyid
and history.state='S'
and history.record# = entry.id);
You can use oracle sysdate function to get the current date and time of the database server, and use it in the query.
select distinct companyid
from frequency
where exists
(select 1
from history, entry
where history.companyid = entry.companyid
and history.state='S'
and history.recordno = entry.id)
and sysdate > to_date(date,'DD-MM-YYYY');
Isn't this a simple join of the three tables?
select distinct e.company_id
from entry e
join history h on h.companyid = e.companyid and h,recordno = e.id
join frequency f on f.companyid = e.companyid and f.task = e.task
where f.date < trunc(sysdate)
and h.state = 'S';
or with the criteria in the ON clauses:
select distinct e.company_id
from entry e
join history h on h.companyid = e.companyid and h,recordno = e.id and h.state = 'S'
join frequency f on f.companyid = e.companyid and f.task = e.task and f.date < trunc(sysdate)
;
Usually, though, you'd have a company table, so you wouldn't need DISTINCT:
select * from company where companyid in (select e.companyid from ...);
Related
I'm having a hard time setting up a query(select). Database is not my specialty, so I'm turning to the experts. Let me show what I need.
----companies--- ----company_server----- -----servers---- -----print------------------------
| id | name | | company | server | | id | name | | id |page|copy | date |server
|----|-------- | |---------|----------| |----|-------- | |----|----|-----|-------------
| 1 | Company1 |1--N| 1 | 1 |N*--1| 1 | Server1 |1--N| 1 | 2 | 3 | 2020-1-11 | 1
| 2 | Company2 | | 2 | 1 | | 2 | Server2 | | 2 | 1 | 6 | 2020-1-12 | 3
| 3 | Company3 | | 3 | 2 | | 3 | Server3 | | 3 | 4 | 5 | 2020-1-13 | 4
| 3 | 3 | | 4 | Server4 | | 4 | 5 | 3 | 2020-1-15 | 2
| 5 | 3 | 4 | 2020-1-15 | 4
| 6 | 1 | 2 | 2020-1-16 | 3
| 7 | 2 | 2 | 2020-1-16 | 4
What I need?
Example where date between CAST(2020-1-12 AS DATE) AND CAST(2020-1-15 AS DATE) group by servers.id
| companies | server | sum | percent
------------------------------------------------------------------------------------
| company1,company2 | server1 | sum(page*copy) = 0 or null | 0 or NULL
| company3 | server2 | sum(page*copy) = 15 | 28.30
| company3 | server3 | sum(page*copy) = 6 | 11.32
| NULL | server4 | sum(page*copy) = 32 | 60.38
Few notes:
I need this query for MYSQL;
Every Company is linked to at least one server.
I need result grouped by server. So, every company linked to that server must be concatenated by a comma.
If the company has not yet been registered, the value null should be presented.
The sum (page * copie) must be presented as zero or null (I don't care) in the case that there was no printing in the date range.
The percentage should be calculated according to the date range entered and not with all records in the database.
The field date is stored as MYSQL DATE.
Experts, I thank you in advance for your help. I currently solve this problem with at least 03 queries to the database, but I have a conviction that I could do it with just one query.
Added a fiddle. Sorry. Im still learing how to use this.
https://www.db-fiddle.com/f/dXej7QCPe9iDopfYd1SfVh/2
Follows the query that more or less represents how far I had arrived. Notice that in the middle of the way 'server4' disappeared because there are no values for it in print in the period searched for him and I am in possession of the total of the period but I cannot calculate the percentage.
i'm stuck
select
*
from
(select
sum(p.copy * p.page) as sum1,
s.name as s_name,
s.id as s_id
from
print p
join servers s on s.id = p.server
where p.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
group by s.id) as t1
join company_server cs on cs.server = t1.s_id
right join companies c on c.id = cs.company
cross join(
select
sum(p1.copy * p1.page) sum2
from
print p1
where p1.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
) as c;
I did this query before you add fiddle, so may be name of column of mine is not same as you. Anyway, this is my solution, hope it help you.
select group_concat(c.name separator ',') as name_company,
ss.name,
sum_print as sum,
(sum_print/total) *100 as percentage
from companies c
inner join company_server cs on c.id = cs.company
right join servers ss on ss.id = cs.id
left join
(
select server,sum(page*copy) as sum_print, date from print
where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)
group by server
) tmp on tmp.server = ss.id
cross join
(select sum(page*copy) as total from print where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)) tmp2
group by id
Group and concat by comma, using GROUP_CONCAT .
You can reference this image for JOIN clause.
https://i.stack.imgur.com/6cioZ.png
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
I am working on a product sample inventory system where I track the movement of the products. The status of each product can have a status of "IN" or "OUT" or "REMOVED". Each row of the table represents a new entry, where ID, status and date are unique. Each product also has a serial number.
I need help with a SQL query that will return all products that are currently "OUT". If I simply just select SELECT * FROM table WHERE status = "IN", it will return all products that ever had status IN.
Every time product comes in and out, I duplicate the last row of that specific product and change the status and update the date and it will get a new ID automatically.
Here is the table that I have:
id | serial_number | product | color | date | status
------------------------------------------------------------
1 | K0T4N | XYZ | silver | 2016-07-01 | IN
2 | X56Z7 | ABC | silver | 2016-07-01 | IN
3 | 96T4F | PQR | silver | 2016-07-01 | IN
4 | K0T4N | XYZ | silver | 2016-07-02 | OUT
5 | 96T4F | PQR | silver | 2016-07-03 | OUT
6 | F0P22 | DEF | silver | 2016-07-04 | OUT
7 | X56Z7 | ABC | silver | 2016-07-05 | OUT
8 | F0P22 | DEF | silver | 2016-07-06 | IN
9 | K0T4N | XYZ | silver | 2016-07-07 | IN
10 | X56Z7 | ABC | silver | 2016-07-08 | IN
11 | X56Z7 | ABC | silver | 2016-07-09 | REMOVED
12 | K0T4N | XYZ | silver | 2016-07-10 | OUT
13 | 96T4F | PQR | silver | 2016-07-11 | IN
14 | F0P22 | DEF | silver | 2016-07-12 | OUT
This query will give you all the latest records for each serial_number
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL
Below query will give your expected result
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL AND a.status LIKE 'OUT'
There are two good ways to do this. Which way is best,in terms of performance, can depend on various factors, so try both.
SELECT
t1.*
FROM table t
LEFT OUTER JOIN table later_t
ON later_t.serial_number = t.serial_number
AND later_t.date > t.date
WHERE later_t.id IS NULL
AND t.status = "OUT"
Which column you check from later_t for IS NULL does not matter, so long as that column is declared NOT NULL in the table definition.
The other logically equivalent method is:
SELECT
t.*
FROM table t
INNER JOIN (
SELECT
serial_number,
MAX(date) AS date
FROM table
GROUP BY serial_number
) latest_t
ON later_t.serial_number = t.serial_number
AND latest_t.date = t.date
WHERE t.status = "OUT"
For each of these queries, I strongly suggest the following index:
ALTER TABLE table
ADD INDEX `LatestSerialStatus` (serial_number,date)
I use this type of query a lot in my own work, and have the above index as the primary key on tables. Query performance is extremely fast in such cases, for these type of queries.
See also the documentation on this query type.
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 have a MySQL table called "objecttable" that has the following structure and data in it. (The data is just a sequence, there is a whole lot more).
ID | Name | posX | posY | posZ |rotX | rotY | rotZ | rotW |
3562 | LODpmedhos1_LAe | 2062 | -1703 | 16 | 0 | 45 | 22 | 1 |
3559 | LODpmedhos5_LAe | 2021 | -1717 | 15 | 0 | 45 | 34 | 1 |
3561 | LODpmedhos3_LAe | 2021 | -1717 | 15 | 0 | 45 | 34 | 1 |
I want to figure out which records have the same posX, posY, posZ, rotX, rotY and rotZ values and insert them into a table called "matchtable", and in the end I want it to look like this (I have the table structure ready)
ID1 | Name | ID2 | Name |
3559 | LODpmedhos5_LAe | 3561 | LODpmedhos3_LAe|
I'd appreciate if someone could give me the correct SQL query for it. I don't have more than two matching coordinates and not all coordinates match.
Sorry if the table representations suck, I'll try to make a HTML table if necessary.
Thanks!
This query will do the trick, but the number of results might be a LOT more than required. For example, if there are 5 rows satisfying your query, then the results will be 20( = n*(n-1) ) in number.
SELECT ot.ID AS ID1, ot.Name AS Name1, ot2.ID AS ID2, ot2.Name AS Name
FROM objecttable ot
JOIN objecttable ot2
ON ot.ID > ot2.ID
AND ot.posX = ot2.posX
AND ot.posY = ot2.posY
AND ot.posZ = ot2.posZ
AND ot.rotX = ot2.rotX
AND ot.rotY = ot2.rotY
AND ot.rotZ = ot2.rotZ
EDIT
In reply to lserni's comment:
ON ot.ID <> ot2.ID
The above condition is there to remove the result like:
ID1 | Name | ID2 | Name |
3559 | LODpmedhos5_LAe | 3559 | LODpmedhos5_LAe|
try this:
-- insert into matchtable -- uncomment to insert the data
select alias1.Id,
alias1.Name,
alias2.Id
alias2.Name
from objecttable as alias1
join objecttable as alias2
on alias1.posx = alias2.posx
and alias1.posy = alias2.posy
and alias1.posz = alias2.posz
and alias1.roty = alias2.roty
and alias1.roty = alias2.roty
and alias1.rotz = alias2.rotz
and alias1.Id > alias2.Id