I am using MySQL and I am trying to get data out of three different tables but am not sure of the syntax that I should use.
I have a table called full_daily_data_1 with the following fields
Symbol
Trade_Date
Day_Open
Day_High
I also have a table called custom_indices_xref with the following fields:
symbol
Custom_Index_Name
And a table called daily_index_weightings with the following fields:
Custom_Index_Name
Symbol
Trade_date
combo_weighting
Right now, I have this as a select statement to get the data I need out of two tables:
SELECT
Symbol,
Trade_Date,
Day_Open,
Day_High
FROM
full_daily_data_1
WHERE
trade_date >= '2012/01/01' AND
trade_date <= '2012/01/31' AND
symbol in (SELECT symbol from custom_indices_xref WHERE Custom_Index_Name = 'Agricultural-Chemical-and-Fertilizer-Stocks');
But what I want is the following data for each symbol for each date in the date range
symbol
Day_Open
Day_High,
Custom_Index_Name
Symbol
Trade_date
combo_weighting
So basically, needing to add combo_weighting from the daily_index_weightings table for the selected days and symbols. What should my SQL statement look like to accomplish this?
I tried this, but I get a SQL syntax error, so not sure what I'm doing wrong:
SELECT
full_daily_data_1.Symbol,
full_daily_data_1.Trade_Date,
full_daily_data_1.Day_Open,
full_daily_data_1.Day_High,
full_daily_data_1.Day_Low,
daily_index_weightings.combo_weighting
FROM
full_daily_data_1
WHERE
trade_date >= '2012/01/01' AND
trade_date <= '2012/01/31' AND
Symbol in (SELECT symbol from custom_indices_xref WHERE Custom_Index_Name = 'Agricultural-Chemical-and-Fertilizer-Stocks')
JOIN
daily_index_weightings ON
daily_index_weightings.symbol = full_daily_data_1.Symbol AND
daily_index_weightings.Trade_Date = full_daily_data_1.Trade_Date ;
SELECT fdd.Symbol, fdd.Trade_Date, fdd.Day_Open, fdd.Day_High, fdd.Day_Low, diw.combo_weighting
FROM full_daily_data_1 fdd
INNER JOIN custom_indicies_xref cix ON fdd.symbol=cix.symbol
INNER JOIN daily_index_weighings diw ON fdd.symbol = diw.symbol
WHERE
trade_date >= '2012/01/01' AND
trade_date <= '2012/01/31' AND
cix.Custom_Index_Name = 'Agricultural-Chemical-and-Fertilizer-Stocks'
The INNER JOINS could be changed to LEFT JOIN if required.
Alternatively, since your second and third tables also match the Custom_Index_Name fields (which is bad design, by the way):
SELECT fdd.Symbol, fdd.Trade_Date, fdd.Day_Open, fdd.Day_High, fdd.Day_Low, diw.combo_weighting
FROM full_daily_data_1 fdd
INNER JOIN (custom_indicies_xref cix ON fdd.symbol=cix.symbol
INNER JOIN daily_index_weighings diw ON cix.Custom_Index_Name = diw.Custom_Index_Name)
ON fdd.symbol = cix.symbol
WHERE
trade_date >= '2012/01/01' AND
trade_date <= '2012/01/31' AND
cix.Custom_Index_Name = 'Agricultural-Chemical-and-Fertilizer-Stocks'
Your sql clauses are out of order and you should use alias for your table names, just makes it easier to read. Another thing I noticed is your fields in your WHERE clause are ambiguous.
SELECT
FDD.Symbol,
FDD.Trade_Date,
FDD.Day_Open,
FDD.Day_High,
FDD.Day_Low,
DIW.combo_weighting
FROM
full_daily_data_1 FDD
JOIN
daily_index_weightings DIW ON
DIW.symbol = FDD.Symbol AND
DIW.Trade_Date = FDD.Trade_Date ;
WHERE
FDD.trade_date >= '2012/01/01' AND
FDD.trade_date <= '2012/01/31' AND
FDD.Symbol in
(SELECT symbol from custom_indices_xref WHERE Custom_Index_Name = 'Agricultural-Chemical-and-Fertilizer-Stocks')
Related
I want to export some data from the DB.
Basically what I want to say is this:
1- Select mbr_name from the members table
2- Choose the ones that exist at the course_registration table (based on mbr_id)
3- Join the course_registration ids with course_comments table
Then I need to apply these WHERE condtions as well:
1- Make sure that crr_status at course_registration table is set to completed
2- Make sure that crr_ts at course_registration table is between "2021-03-07 00:00:00" AND "2022-03-17 00:00:00"
3- Make sure that crm_confirmation from course_comments table is set to accept
So I tried my best and wrote this:
SELECT members.mbr_name
FROM members
INNER JOIN course_registration AS udt ON members.mbr_id = udt.crr_mbr_id
INNER JOIN course_comments AS dot ON udt.crr_cor_id = dot.crm_reference_id
WHERE udt.crr_status = "completed" AND udt.crr_ts >= "2021-03-07 00:00:00" AND udt.crr_ts < "2022-03-17 00:00:00"
AND dot.crm_confirmation = "accept";
But this will give wrong data somehow.
The actual number of members that have all these conditions are 12K but this query gives me 120K results which is obviously wrong!
So what's going wrong here? How can I solve this issue?
UPDATE:
Here are the keys of each table:
members (mbr_id (PK), mbr_name)
course_registration (crr_id (PK), crr_mbr_id (FK), crr_cor_id (FK), crr_status)
course_comments (crm_id (PK), crm_reference_id (FK), crm_confirmation)
You have a so-called cardinality problem. JOINs can, when multiple rows on the one table match a single row in the other table, cause the result set to have multiple rows. Your JOIN as written will generate many rows: members x courses x comments. That's what JOIN does.
It looks like you want exactly one row in your resultset for each member who ...
has completed one or more courses meeting your criterion.
has submitted one or more comments.
So let's start with a subquery. It gives the mbr_id values for members who have submitted one or more comments on one or more courses that meet your criteria.
SELECT udt.crr_mbr_id
FROM course_registration udt
JOIN course_comments dot ON udt.crr_cor_id = dot.crm_reference_id
WHERE udt.crr_status = "completed"
AND udt.crr_ts >= "2021-03-07 00:00:00"
AND udt.crr_ts < "2022-03-17 00:00:00"
AND dot.crm_confirmation = "accept"
GROUP BY udt.mbr_id
You use the results of that subquery to find your members. The final query is
SELECT members.mbr_name
FROM members
WHERE members.mbr_id IN (
SELECT udt.crr_mbr_id
FROM course_registration udt
JOIN course_comments dot ON udt.crr_cor_id = dot.crm_reference_id
WHERE udt.crr_status = "completed"
AND udt.crr_ts >= "2021-03-07 00:00:00"
AND udt.crr_ts < "2022-03-17 00:00:00"
AND dot.crm_confirmation = "accept"
GROUP BY udt.mbr_id )
As you only want to select Member name you can try as below if this gives required result
select m.mbr_name
from Members m
where Exists ( select 1 from Course_Registration cr
join Course_Comments cm on cr.crr_cor_id = cm.crm_reference_id
where cr.crr_mbr_id = m.mbr_id
And cr.crr_status = "completed" AND cr.crr_ts >= "2021-03-07 00:00:00" AND cr.crr_ts < "2022-03-17 00:00:00"
AND cr.crm_confirmation = "accept";
);
My first guess, without knowing the context, is that:
a member can register to one or more courses,
each course can have one or more comments.
If this is the case, you are getting way more tuples due to redundancy. In that case you just need to stick a DISTINCT right after your first SELECT.
Furthermore, since the JOIN is the most resource-expensive operation in sql, I would first filter the data and then leave any join as the last operation to improve efficiency. Something like this:
SELECT
members.mbr_name
FROM
(
SELECT DISTINCT
crm_reference_id
FROM
course_comments
WHERE
crm_confirmation = 'accept'
) accepted_comments
INNER JOIN
(
SELECT DISTINCT
crr_mbr_id,
crr_cor_id
FROM
course_registration
WHERE
crr_status = 'completed'
AND
crr_ts BETWEEN '2021-03-07 00:00:00' AND '2022-03-17 00:00:00'
) completed_courses
ON
accepted_comments.crm_reference_id = completed_courses.crr_cor_id
INNER JOIN
members
ON
members.mbr_id = completed_courses.crr_mbr_id
I would start at the registration FIRST instead of the members. By getting a DISTINCT list of members signing up for a course, you have a smaller subset. From that too, joining to the comments for just those accepted gives you a final list.
Once you have those two, join back to members to get the name. I included the member ID as well as the name because what if you have two or more "John" or "Karen" names in the registration. At least you have the ID that confirms the unique students.
select
m.mbr_name,
m.mbr_id
from
( select distinct
cr.crr_mbr_id
from
course_registration cr
JOIN course_comments cc
ON cr.crr_cor_id = cc.crm_reference_id
AND cc.crm_confirmation = 'accept'
WHERE
cr.crr_status = 'completed'
AND cr.crr_ts >= '2021-03-07'
AND cr.crr_ts < '2022-03-17' ) PQ
JOIN members m
ON PQ.crr_mbr_id = m.mbr_id
Try using this and if it not works then try using 'between' for date field (crr_ts).
select mbr.mbr_name from
(
select * from course_registration AS udt
INNER JOIN course_comments AS dot ON udt.crr_cor_id = dot.crm_reference_id
where dot.crm_confirmation = "accept" AND udt.crr_status = "completed" AND udt.crr_ts >= "2021-03-07 00:00:00" AND udt.crr_ts < "2022-03-17 00:00:00"
)x
INNER JOIN members mbr on mbr.mbr_id = x.crr_mbr_id
Try this:
SELECT *
FROM members M
INNER JOIN course_registration CR
ON CR.crr_mbr_id = M.mbr_id
AND CR.crr_status = 'completed'
AND CR.crr_ts BETWEEN '2021-03-07 00:00:00' AND '2022-03-17 00:00:00'
WHERE EXISTS(
SELECT * FROM course_comments CC
WHERE CC.crm_confirmation = 'accept'
AND CC.crm_reference_id = CR.crr_cor_id
)
ORDER BY M.mbr_id;
I'm not strong in DB at all and I need your help.
I need SQL request with GROUP by twice.
Example of my data in table
<table border="1" style="border-collapse:collapse">
<tr><th>id</th><th>market_id</th><th>price</th><th>low</th><th>high</th><th>symbol</th><th>created_at</th></tr>
<tr><td>1</td><td>1</td><td>5773.8</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:10</td></tr>
<tr><td>2</td><td>1</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:11</td></tr>
<tr><td>3</td><td>1</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:12</td></tr>
<tr><td>4</td><td>2</td><td>5771</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:18</td></tr>
<tr><td>5</td><td>2</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:18</td></tr>
<tr><td>6</td><td>2</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:19</td></tr>
<tr><td>7</td><td>1</td><td>5773.1</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:25</td></tr>
<tr><td>8</td><td>1</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:25</td></tr>
<tr><td>9</td><td>1</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:26</td></tr>
<tr><td>10</td><td>2</td><td>5773.1</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:32</td></tr>
<tr><td>11</td><td>2</td><td>0.42741</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:32</td></tr>
<tr><td>12</td><td>2</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:33</td></tr></table>
I would like to get latest data for every market_id and symbol
That's mean I need somethind like that in the end :
- id market_id symbol
- 7 1 btcusd
- 8 1 iotusd
- 9 1 xrpusd
- 10 2 btcusd
- 11 2 iotusd
- 12 2 xrpusd
Really need help, a little bit blocked.
You are almost there. Try this
SELECT c.*
FROM CRYPTO as C
JOIN (
SELECT market_id, symbol, MAX(id) as maxid
FROM CRYPTO
GROUP BY market_id, symbol
) AS C2
ON C2.maxid = C.id and C.market_id = c2.market_id and c.symbol = c2.symbol
Along these lines...
SELECT MAX(id), market_id, symbol
FROM crypto
GROUP BY market_id, symbol
Here's my comment stated as SQL.
SELECT A.ID, A.MarketID, A.Symbol, A.Price, A.Low, A.High
FROM CRYPTO A
INNER JOIN (SELECT max(Created_at) MCA, Market_ID, Symbol
FROM crypto
GROUP BY Market_ID, Symbol) B
on A.Created_At = B.MCA
and A.market_ID = B.Market_ID
and A.Symbol = B.Symbol
What this does:
The derived table (aliased B) generates 1 line for each market_ID and symbol having the max created_at time. It then uses this derived table set to join back to the base set (aliased A) to limit the data to just those having the max created_at. this allows us to show the whole record from A for each unique market_Id and symbol; but only for records having the max created_at.
Other engines would allow you to use a cross apply or an analytic to obtain the desired results.
I tried these requests
SELECT * FROM CRYPTO as C3
JOIN (
SELECT MAX(id) as max
FROM CRYPTO as C1
GROUP BY symbol
) AS C2
ON C2.max = C3.id
SELECT M.id, M.name, R.symbol FROM MARKET AS M
JOIN (
SELECT DISTINCT C.symbol, C.market_id
FROM CRYPTO as C
) as R
ON M.id = R.market_id
But finally I did not find the good combination.
I would like to update a column from a table, and the data i want to put in is the result of a mathematical operation using subquerys that references the update table.
The problem is I want to update every row using data from the same row in the mathematical operation mentioned above.
Here's the example:
UPDATE licenses as lic
SET lic.numSessions =
(
select difference from
(select
(
select (p.numSessions * p.numMonth) as num from products p
inner join licenses l on p.idProduct = l.idProduct and l.idpatient = lic.idPatient and l.currentLicense = 1
)
-
(
SELECT COUNT(distinct s.idSession) as num
FROM sessions s
WHERE s.idPatient = lic.idPatient
AND s.dateValue >= (select l.dateCreated from licenses l where l.idPatient = lic.idPatient and l.currentLicense = 1) AND s.status = 2
)
as difference
)
x
);
EDIT:
Example of what i want:
Every row of 'licenses' has a 'idPatient'. Let's call it 'X'.
I want to see how many sessions X has done (for example 10) and then substract this number from the total number of sessions of 'X's' product have (for example 50). So the result in the update for X will be: 50 - 10 = 40.
The subqueries alone work perfectly, I'have the value of 50, then the value of 10, and then when I try to substract I have the value 40 as a column named 'difference'.
The problem I've got is that the query can't recognize the value 'lic.idPatient' inside the first subquery in the substract operation:
/* SQL Error (1054): Unknown column 'lic.idPatient' in 'on clause' */
Thanks in advance and sorry for my writing, I'm not native English.
You have to write query like this
UPDATE licenses AS licNew
SET licNew.numSessions =
(
SELECT x.difference FROM
((SELECT
(
SELECT (p.numSessions * p.numMonth) AS num FROM products p
INNER JOIN licenses l ON p.idProduct = l.idProduct AND l.idpatient = lic.idPatient AND l.currentLicense = 1
)
-
(
SELECT COUNT(DISTINCT s.idSession) AS num
FROM sessions s
WHERE s.idPatient = 6361
AND s.dateValue >= (SELECT l.dateCreated FROM licenses l WHERE l.idPatient = 6361 AND l.currentLicense = 1) AND s.status = 2
))
AS difference, lic.idPatient
FROM licenses lic
)
X WHERE licNew.idPatient = x.idPatient
);
In general, you cannot modify a table and select from the same table in a subquery.
Please refer this link.
am new here so please redirect me if am posting this wrong
sorry i can't post images yet
I have this table , I want to select all where type = 'Maladie'
but ... if a row with type = 'Reprise' that contains a date between any of the maladie type ones then i must show
maladie row date start -> Reprise rows date start
instead of
maladie row date start -> maladie row date end
so result for that image would look like this
note: rows with type Reprise may or may not exist
thnks
Outer join the reprise records. Where you find a match use its dates, where you don't use the original dates:
select
m.mat,
m.start,
coalesce(r.end, m.end) as end,
m.number_days,
m.number_hours
from (select * from mytable where type = 'Maladie') m
left join (select * from mytable where type = 'Reprise') r
on r.mat = m.mat and r.start between m.start and m.end;
If there can be more than one reprise record per maladie date range and you want to take the first one then, use:
select
m.mat,
m.start,
coalesce(r.repday, m.end) as end,
m.number_days,
m.number_hours
from (select * from mytable where type = 'Maladie') m
left join
(
select mat, min(end) as repday
from mytable
where type = 'Reprise'
group by mat
) r on r.mat = m.mat and r.repday between m.start and m.end;
You need to create self join and filter type that use min to catch first date if there is more that one reprise
SELECT a.mat
,a.start
,min(coalesce(b.END, a.END)) AS END
,a.number_of_days
,a.number_of_hours
,type
FROM table1 a
LEFT JOIN (
SELECT mat
,start AS start
,END AS END
FROM table1 t
WHERE t.type = 'Reprise'
) b ON b.start BETWEEN a.start
AND a.END and a.mat=b.mat
WHERE type = 'Malaide'
GROUP BY a.mat
,a.start
ORDER BY start
I want to rewrite this query so it would be working with table and column alias, but I can't get it working. After some research I found that we cannot use table alias in WHERE clause, I tried to change WHERE to HAVING, but still not working ...
Does anyone have a better idea?
This is the query:
SELECT pos.pat_id+1000 as Barcode
,pat_sex as Sex
,pat_dob as DoB
,Test
,Required_diagnosis
,Sample_date
,Location
,DQ_list
FROM (SELECT icv_pat_id as pat_id
,icv_test_name as Test
,'Trichomonas' as Required_diagnosis
,lrr_request_date as Sample_date
,d.Location
,d.DQ_List
FROM investigation_component_values
INNER JOIN lab_result_records l on l.lrr_rrc_id = icv_rrc_id
LEFT JOIN lookups.dq_lists d ON d.lrr_ordering_location_name = l.lrr_ordering_location_name
WHERE lrr_request_date BETWEEN '2015-08-01' AND '2015-08-31'
AND icv_pat_id NOT IN (SELECT pat_id FROM cnwlreports.test_patients)
AND icv_ist_id = 83882
AND icv_non_quantifiable_result = 'Trichomonas vaginalis ISOLATED') as pos
LEFT JOIN trungnguyen.patients pat on pos.pat_id = pat.pat_id
WHERE pos.pat_id NOT IN (SELECT DISTINCT p.pat_id
FROM pos p
LEFT JOIN (SELECT rrc_pat_id as pat_id
,rrc_clinic_date as diag_date
FROM referral_records
WHERE rrc_status = 'approved'
AND rrc_answered_id = 17380
AND rrc_clinic_date BETWEEN DATE_SUB('2015-08-01',INTERVAL 7 DAY)
AND DATE_ADD('2015-08-31',INTERVAL 7 DAY)
AND rrc_pat_id IN (SELECT pat_id FROM pos)
GROUP BY rrc_pat_id
,rrc_clinic_Date) d ON p.pat_id = d.pat_id
WHERE ABS(datediff(sample_date,diag_date)) < 7
)
;
You are confusing table aliases and column aliases. A column alias is defined in a SELECT and cannot be used in the WHERE clause for that SELECT.
A table alias is defined in the FROM clause. It can -- and should -- be used whenever columns from that table are used. Such a column name, with a table alias, is called a qualified column.
For example, from your subquery:
SELECT icv.icv_pat_id as pat_id,
icv.icv_test_name as Test,
'Trichomonas' as Required_diagnosis,
lrr.lrr_request_date as Sample_date
. . .
FROM investigation_component_values icv INNER JOIN
lab_result_records lrr
on lrr.lrr_rrc_id = icv.icv_rrc_id
WHERE lrr.lrr_request_date BETWEEN '2015-08-01' AND '2015-08-31' . . .
pat_id, test, Required_diagnosis and Sample_date are all column alias. icv and lrr are table aliases. In the WHERE clause, you can have:
WHERE lrr.lrr_request_date BETWEEN '2015-08-01' AND '2015-08-31' . . .
But you cannot have:
WHERE Sample_Date BETWEEN '2015-08-01' AND '2015-08-31' . . .
You can use the real column name, so in the 'WHERE pos.pat_id' for example, You should use: 'WHERE pos.icv_pat_id'.
generally you can use alias for table in where clause;
Normally , I do:
Select * from Table T, Table2 T2 WHERE T.key = T2.key;
For example, try to explicit inner join with this syntax.