When a user buys a license for my product I'm trying to add the number of days remaining (if he has any) to the new 30-day period.
SELECT
SUM(DATEDIFF(access_expires, CURDATE())) + 30 as access_remaining
FROM wp_woocommerce_downloadable_product_permissions
WHERE
user_email = 'user#mail.com' AND
product_id = 8 AND access_expires IS NOT NULL;
UPDATE wp_woocommerce_downloadable_product_permissions
SET
access_expires = DATE_ADD(CURDATE(), access_remaining)
WHERE
user_email = 'user#mail.com' AND
product_id = 8 AND
access_expires IS NULL
It seems like I can't use access_remaining from my previous query in my second query. Please help, if possible without using join. Using mariadb if it means anything.
You seem to have imposed odd restrictions on this problem. Are you using strictly /usr/bin/mysql, and cannot use e.g. python or java? Can you assign to a temp variable with := ? Why no JOIN?
Consider using UPDATE with correlated sub-query, as in MySQL/SQL: Update with correlated subquery from the updated table itself .
You chose a perfectly sensible way to model the bookkeeping, by maintaining a calendar expiry date. But consider modeling in a different way: you might store (begin, end) dates for which a customer is paid up. You might find it makes some queries easier to phrase.
Notice the use of a "user variable" #ar:
SELECT
#ar := SUM(DATEDIFF(access_expires, CURDATE())) + 30 as access_remaining
FROM wp_woocommerce_downloadable_product_permissions
WHERE
user_email = 'user#mail.com' AND
product_id = 8 AND access_expires IS NOT NULL;
UPDATE wp_woocommerce_downloadable_product_permissions
SET
access_expires = DATE_ADD(CURDATE(), #ar)
WHERE
user_email = 'user#mail.com' AND
product_id = 8 AND
access_expires IS NULL
I suggest that SUM does not make sense in this context.
Wouldn't it be simpler to just do this?
UPDATE wp_woocommerce_downloadable_product_permissions
SET
access_expires = GREATEST(access_expires, CURDATE()) + INTERVAL 30 DAY
WHERE
user_email = 'user#mail.com' AND
product_id = 8 AND
access_expires IS NULL
Related
I want to update multiple columns in a table using a correlated subquery. Updating a single column is straightforward:
UPDATE route
SET temperature = (SELECT amb_temp.temperature
FROM amb_temp.temperature
WHERE amb_temp.location = route.location)
However, I'd like to update several columns of the route table. As the subquery is much more complex in reality (JOIN with a nested subquery using SpatiaLite functions), I want to avoid repeating it like this:
UPDATE route
SET
temperature = (SELECT amb_temp.temperature
FROM amb_temp.temperature
WHERE amb_temp.location = route.location),
error = (SELECT amb_temp.error
FROM amb_temp.temperature
WHERE amb_temp.location = route.location),
Ideally, SQLite would let me do something like this:
UPDATE route
SET (temperature, error) = (SELECT amb_temp.temperature, amb_temp.error
FROM amb_temp.temperature
WHERE amb_temp.location = route.location)
Alas, that is not possible. Can this be solved in another way?
Here's what I've been considering so far:
use INSERT OR REPLACE as proposed in this answer. It seems it's not possible to refer to the route table in the subquery.
prepend the UPDATE query with a WITH clause, but I don't think that is useful in this case.
For completeness sake, here's the actual SQL query I'm working on:
UPDATE route SET (temperature, time_distance) = ( -- (C)
SELECT -- (B)
temperature.Temp,
MIN(ABS(julianday(temperature.Date_HrMn)
- julianday(route.date_time))) AS datetime_dist
FROM temperature
JOIN (
SELECT -- (A)
*, Distance(stations.geometry,route.geometry) AS distance
FROM stations
WHERE EXISTS (
SELECT 1
FROM temperature
WHERE stations.USAF = temperature.USAF
AND stations.WBAN_ID = temperature.NCDC
LIMIT 1
)
GROUP BY stations.geometry
ORDER BY distance
LIMIT 1
) tmp
ON tmp.USAF = temperature.USAF
AND tmp.WBAN_ID = temperature.NCDC
)
High-level description of this query:
using geometry (= longitude & latitude) and date_time from the route table,
(A) find the weather station (stations table, uniquely identified by the USAF and NCDC/WBAN_ID columns)
closest to the given longitude/latitude (geometry)
for which temperatures are present in the temperature table
(B) find the temperature table row
for the weather station found above
closest in time to the given timestamp
(C) store the temperature and "time_distance" in the route table
I'm making a report in Cognos Report Studio and I'm having abit of trouble getting a count taht I need. What I need to do is count the number of IDs for a department. But I need to split the count between initiated and completed. If an ID occures more than once, it is to be counted as completed. The others, of course, will be initiated. So I'm trying to count the number of ID occurences for a distinct ID. Here is the query I've made in SQl Developer:
SELECT
COUNT((CASE WHEN COUNT(S.RFP_ID) > 8 THEN MAX(CT.GCT_STATUS_HISTORY_CLOSE_DT) END)) AS "Sales Admin Completed"
,COUNT((CASE WHEN COUNT(S.RFP_ID) = 8 THEN MIN(CT.GCT_STATUS_HISTORY_OPEN_DT) END)) as "Sales Admin Initiated"
FROM
ADM.B_RFP_WC_COVERAGE_DIM S
JOIN ADM.B_GROUP_CHANGE_REQUEST_DIM CR
ON S. RFP_ID = CR.GCR_RFP_ID
JOIN ADM.GROUP_CHANGE_TASK_FACT CT
ON CR.GROUP_CHANGE_REQUEST_KEY = CT.GROUP_CHANGE_REQUEST_KEY
JOIN ADM.B_DEPARTMENT_DIM D
ON D.DEPARTMENT_KEY = CT.DEPARTMENT_RESP_KEY
WHERE CR.GCR_CHANGE_TYPE_ID = '20'
AND S.RFP_LOB_IND = 'WC'
AND S.RFP_AUDIT_IND = 'N'
AND CR.GCR_RECEIVED_DT BETWEEN '01-JAN-13' AND '31-DEC-13'
AND D.DEPARTMENT_DESC = 'Sales'
AND CT.GCT_STATUS_IND = 'C'
GROUP BY S.RFP_ID ;
Now this works. But I'm not sure how to translate taht into Cognos. I tried doing a CASE taht looked liek this(this code is using basic names such as dept instead of D.DEPARTMENT_DESC):
CASE WHEN dept = 'Sales' AND count(ID for {DISTINCT ID}) > 1 THEN count(distinct ID)END)
I'm using count(distinct ID) instead of count(maximum(close_date)). But the results would be the same anyway. The "AND" is where I think its being lost. It obviously isn't the proper way to count occurences. But I'm hoping I'm close. Is there a way to do this with a CASE? Or at all?
--EDIT--
To make my question more clear, here is an example:
Say I have this data in my table
ID
---
1
2
3
4
2
5
5
6
2
My desired count output would be:
Initiated Completed
--------- ---------
4 2
This is because two of the distinct IDs (2 and 5) occure more than once. So they are counted as Completed. The ones that occure only once are counted as Initiated. I am able to do this in SQl Dev, but I can't figure out how to do this in Cognos Report Studio. I hope this helps to better explaine my issue.
Oh, I didn't quite got it originally, amending the answer.
But it's still easiest to do with 2 queries in Report Studio. Key moment is that you can use a query as a source for another query, guaranteeing proper group by's and calculations.
So if you have ID list in the table in Report Studio you create:
Query 1 with dataitems:
ID,
count(*) or count (1) as count_occurences
status (initiated or completed) with a formula: if (count_occurences > 1) then ('completed') else ('initiated').
After that you create a query 2 using query one as source with just 2 data items:
[Query1].[Status]
Count with formula: count([Query1].[ID])
That will give you the result you're after.
Here's a link to doco on how to nest queries:
http://pic.dhe.ibm.com/infocenter/cx/v10r1m0/topic/com.ibm.swg.ba.cognos.ug_cr_rptstd.10.1.0.doc/c_cr_rptstd_wrkdat_working_with_queries_rel.html?path=3_3_10_6#cr_rptstd_wrkdat_working_with_queries_rel
I'm using an SQLite query (in an iOS application) as follows:
SELECT * FROM tblStations WHERE StationID IN ('206','114','113','111','112','213','214','215','602','603','604')
However, I'm getting the resulting data in either descending or ascending order, when what I really want is for the data to be returned in the order I've specified in the IN clause.
Is this possible?
A trivial way to sort the results
NSArray *stationIDs = #[#206,#114,#113,#111,#112,#213,#214,#215,#602,#603,#604];
NSArray *stations = #[#{#"Id":#(604)},#{#"Id":#(603)},#{#"Id":#(602)},#{#"Id":#(215)},
#{#"Id":#(214)},#{#"Id":#(213)},#{#"Id":#(112)},#{#"Id":#(111)},
#{#"Id":#(113)},#{#"Id":#(114)},#{#"Id":#(206)}];
stations = [stations sortedArrayUsingComparator:
^NSComparisonResult(NSDictionary * dict1, NSDictionary *dict2)
{
NSUInteger index1 = [stationIDs indexOfObject:dict1[#"Id"]];
NSUInteger index2 = [stationIDs indexOfObject:dict2[#"Id"]];
return [#(index1) compare:#(index2)];
}];
You could use a CASE expression to map these station IDs to another value that is suitable for sorting:
SELECT *
FROM tblStations
WHERE StationID IN ('206','114','113','111','112',
'213','214','215','602','603','604')
ORDER BY CASE StationID
WHEN '206' THEN 1
WHEN '114' THEN 2
WHEN '113' THEN 3
WHEN '111' THEN 4
WHEN '112' THEN 5
WHEN '213' THEN 6
WHEN '214' THEN 7
WHEN '215' THEN 8
WHEN '602' THEN 9
WHEN '603' THEN 10
WHEN '604' THEN 11
END
I don't believe there's any means of returning SQL data in an order that isn't ascending, descending or random (either intentionally so, or simply in the order the database engine chooses to return the data).
As such, it would probably make sense to simply fetch all of the data returned by the SQLite query and store it in an NSDictionary keyed on the StationID value. It would then be trivial to retrieve in the order you require.
add an additional column to use for sorting. e.g. add a column named "sortMePlease". Fill this column according to your needs, meaning for the row for stationID 216 enter 1, for 114 enter 2, .... and finally add "ORDER BY sortMePlease ASC" to your query.
A second way of doing it (the first one being with CASE WHEN ... THEN END as already stated in another answer) is:
ORDER BY StationID=206 DESC,
StationID=114 DESC,
StationID=113 DESC,
StationID=111 DESC,
StationID=112 DESC,
StationID=213 DESC,
etc.
I am trying to update Table B of a database looking like this:
Table A:
id, amount, date, b_id
1,200,6/31/2012,1
2,300,6/31/2012,1
3,400,6/29/2012,2
4,200,6/31/2012,1
5,200,6/31/2012,2
6,200,6/31/2012,1
7,200,6/31/2012,2
8,200,6/31/2012,2
Table B:
id, b_amount, b_date
1,0,0
2,0,0
3,0,0
Now with this query I get all the data I need in one select:
SELECT A.*,B.* FROM A LEFT JOIN B ON B.id=A.b_id WHERE A.b_id>0 GROUP BY B.id
id, amount, date, b_id, id, b_amount, b_date
1,200,6/31/2012,1,1,0,0
3,400,6/29/2012,1,1,0,0
Now, I just want to copy the selected column amount to b_amount and date to b_date
b_amount=amount, b_date=date
resulting in
id, amount, date, b_id, id, b_amount, b_date
1,200,6/31/2012,1,1,200,6/31/2012
3,400,6/29/2012,1,1,400,6/29/2012
I've tried COALESCE() without success.
Does someone experienced have a solution for this?
Solution:
Thanks to the answers below, I managed to come up with this. It is probably not the most efficient way but it is fine for a one time only update. This will insert for you the first corresponding entry of each group.
REPLACE INTO A SELECT id, amount, date FROM
(SELECT A.id, A.amount, B.id as Bid FROM A INNER JOIN B ON (B.id=A.B_id)
ORDER BY A.id DESC)
GROUP BY Bid;
So what you are looking for seems to be a JOIN inside of an UPDATE query. In mySQL you would use
UPDATE B INNER JOIN A ON B.id=A.b_id SET B.amount=A.amount, B.date=A.date;
but this is not supported by sqlite as this probably related question points out. However, there is a workaround using REPLACE:
REPLACE INTO B
SELECT B.id, A.amount, A.date FROM A
LEFT JOIN B ON B.id=A.b_id
WHERE A.b_id>0 GROUP BY B.id;
The query will simply fill in the values of table B for all columns which should keep their state and fill in the values of table A for the copied values. Make sure the order of the columns in the SELECT statement meet your column order of table B and all columns are mentioned or you will loose these field's data. This is probably dangerous for future changes on table B. So keep in mind to change the column order/presence of this query when changing table B.
Something a bit off topic, because you did not ask for that: A.b_id is obviously a foreign key to B.id. It seems you are using the value 0 for the foreign key to express that there is no corresponding entry in B. (Inferred from your SELECT with WHERE A.b_id>0.) You should consider using the null value for that. When you are using INNER JOIN then instead of LEFT JOIN you can drop the WHERE clause entirely. The DBS will then sort out all unsatisfied relations.
WARNING Some RDBMS will return 2 rows as you show above. Others will return the Cartesian product of the rows i.e. A rows times B rows.
One tricky method is to generate SQL that is then executed
SELECT "update B set b.b_amount = ", a.amount, ", b.b_date = ", a.date,
" where b.id = ", a.b_id
FROM A LEFT JOIN B ON B.id=A.b_id WHERE A.b_id>0 GROUP BY B.id
Now add the batch terminator and execute this SQL. The query result should look like this
update B set b.b_amount = 200, b.b_date = 6/31/2012 where b.id = 1
update B set b.b_amount = 400, b.b_date = 6/29/2012 where b.id = 3
NOTE: Some RDBMS will handle dates differently. Some require quotes.
I would need to get an average from the below linq query but the data field being varchar, i am getting error while doing that.
var _districtResult = (from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
join ml in SessionHandler.CurrentContext.MailingListEntries on ls.SurveyCode equals ml.SurveyCode
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
join chg in SessionHandler.CurrentContext.ChannelGroups on ch.ChannelGroupId equals chg.ChannelGroupId
join tchg in SessionHandler.CurrentContext.ChannelGroups on chg.ParentChannelGroupId equals tchg.ChannelGroupId
where tchg.ParentChannelGroupId == model.LoggedChannelGroupId
select ls).ToList();
ls contains Score1, Score2, Score3, Score4. All these are varchar(50) in the database. Also they allow null values too.
If I need to get an average for Score1 and pass that to my model data, how would i do that?
I tried model.AvgScore1 = _districtResult.Select(m => m.Score1).Average().Value. But i get an error while doing so..
you have to convert (in your example) m.Score1 to a numeric type (here is the MSDN documentation for the Average method. You can not run a mathematical function on string data. you should also probably check for null.
something along the lines of this:
_districtResult.Select(m => string.IsNullOrEmpty(m.Score1) ? 0 : //null, use 0
Double.Parse(m.Score1)).Average() //to double