How to select most recent record while using self join in SQL Server 2014 - sql-server-2014

+-----------+-------------+-------------+----------+
| Item Code | Vendor Code | Direct Cost | Date |
+-----------+-------------+-------------+----------+
| 6994 | 1441 | 3.86 | 20150512 |
| 6994 | 1441 | 3.86 | 20150910 |
| 6994 | 1441 | 4.62 | 20160425 |
| 6994 | 1441 | 4.62 | 20161017 |
| 6994 | 1441 | 4.05 | 20170330 |
| 6994 | 1441 | 4.10 | 20170624 |
+-----------+-------------+-------------+----------+
I am creating a view and I have joined 2 tables PO Header and PO Details, so in this example above I just need 1 line result because Item Code and Vendor Code is repeating, I have achieved this by using the self join on the table but the main requirement is to pick 1 line with most recent date record i.e. 20170624 and Direct Cost is = 4.10
Please see my code below.
l.AUDTDATE is the date column but not in the date format, so I have converted it into date format and in descending order but I am struggling in getting the 1 line with the most recent date.
Thanks
SELECT TOP (100) PERCENT
dbo.NavMdItem.nav_item_code,
dbo.NavMdVendor.nav_vendor_code,
l.ORDERUNIT AS uom,
'' AS variant_code,
h.POCURR AS currency_code,
'1' AS minimum_qty,
l.UNITCOST AS direct_unit_cost,
SUBSTRING(CAST(h.DAYENDDATE AS VARCHAR(50)), 7, 2) + '/' +
SUBSTRING(CAST(h.DAYENDDATE AS VARCHAR(50)), 5, 2) + '/' +
SUBSTRING(CAST(h.DAYENDDATE AS VARCHAR(50)), 0, 5) AS starting_date,
l.DAYENDSEQ,
CONVERT(DATETIME, CAST(l.AUDTDATE AS VARCHAR(50)), 106) AS date
FROM
skh.POPORAH AS h
INNER JOIN
skh.POPORAL AS l ON h.PORAHSEQ = l.PORAHSEQ AND h.DAYENDSEQ = l.DAYENDSEQ
INNER JOIN
dbo.NavMdItem ON l.ITEMNO = dbo.NavMdItem.item_long_code COLLATE Latin1_General_CI_AS
INNER JOIN
dbo.NavMdVendor ON h.VENDOR = dbo.NavMdVendor.vendor_short_code COLLATE Latin1_General_CI_AS
WHERE
(NOT (l.ITEMNO LIKE 'MISC'))
AND (l.ITEMEXISTS = 1)
AND (dbo.NavMdVendor.entity_code = 10)
AND (h.DAYENDDATE > 20150101)
AND (dbo.NavMdItem.nav_item_code = 6499)
AND ((SELECT COUNT(*) AS Expr1
FROM skh.POPORAL AS f
WHERE (DAYENDSEQ = l.DAYENDSEQ)
AND (DAYENDSEQ <= l.DAYENDSEQ)) < 2)
ORDER BY
l.AUDTDATE DESC

You want to group by item code and vendor code and select MAX date.
Is there zero chance of having two on the same date?
UPDATE:
Here is some code that will do what you're wanting. Notice I assumed valid datetime data in the [aud_date] column.
CREATE TABLE #Stuff(
item_code int
,vendor_code int
,direct_cost money
,aud_date datetime
)
INSERT #Stuff VALUES (6994,1441,3.86,'2015-05-12 11:32:12')
INSERT #Stuff VALUES (6994,1441,3.86,'2015-09-10 05:11:00')
INSERT #Stuff VALUES (6994,1441,4.62,'2016-04-25 19:43:43')
INSERT #Stuff VALUES (6994,1441,4.62,'2016-10-17 11:12:13')
INSERT #Stuff VALUES (6994,1441,4.05,'2017-03-30 01:02:03')
INSERT #Stuff VALUES (6994,1441,4.10,'2017-06-24 12:12:12')
SELECT
*
FROM
#Stuff
WHERE
aud_date = (
SELECT MAX(aud_date)
FROM
#Stuff
WHERE
item_code = item_code
AND
vendor_code = vendor_code
GROUP BY
item_code, vendor_code
)
DROP TABLE #Stuff

Related

MySQL updating 2 columns with random and conditional value based on that random

I have such a table, with dates in format YYYY-MM-DD
id | state_id | created_on | closed_on |
1 | 1 | 2020-02-19 | NULL |
2 | 2 | 2020-01-02 | 2020-01-03 |
3 | 1 | 2020-01-05 | NULL |
4 | 4 | 2020-02-07 | 2020-02-08 |
4 | 3 | 2020-02-20 | NULL |
I need to update the STATE with a random state between 2 and 6 WHERE
state_id is 1 OR state_id is 3
created_on is in the past
I can do so with this (IT WORKS PERFECTLY):
UPDATE table SET state_id = FLOOR(2 + rand() * 6)
WHERE created_on < CURRENT_DATE()
AND (state_id=1 OR state_id=3)
What I need to do additionally in the same update, would be to set closed_on:
NULL if the new random state is 3
CURRENT_DATE() if the new random state IS anything but 3
I thought about using SET #newState = FLOOR(2 + rand() * 6), but this way all the records would have the same state_id
is it possible to update with different values?
something like this
UPDATE table SET state_id = FLOOR(2 + rand() * 6), closed_on = IF(new_state_id = 3, NULL, CURRENT_DATE())
WHERE created_on < CURRENT_DATE()
AND (state_id=1 OR state_id=3)
You could self-join the table with a subquery that computes the random number, so you can reuse that value in the outer query:
update mytable t
inner join (
select id, floor(2 + rand() * 6) rnd
from mytable
where state_id in (1, 3)
and created_on < CURRENT_DATE()
) t1 using(id)
set
t.state_id = t1.rnd,
t.closed_on = if(t1.rnd = 3, null, current_date)

How to get percentage of rows which are not NULL in a specific column?

I have a database setup like this:
Table called reviews
+ -------- +
| review |
+ -------- +
| awda |
| ggagw |
| okok |
| ligjr |
| kkfm |
| seff |
| oawr |
| abke |
| (null) |
| (null) |
| (null) |
| (null) |
| (null) |
| (null) |
| (null) |
+ -------- +
How do I get the percentage of how many rows there are, which are NOT NULL?
A basic "formula" of what I want:
percentage = 100 * ( (Sum of rows where review is not null) / (Amount of rows) )
For the example above, this would be:
percentage = 100 * ( ( 8 ) / ( 15) )
= 53.33333333
How can I achieve that by using only one MySQL query?
I think the simplest way is:
select avg( review is not null ) * 100
from reviews;
MySQL treats boolean expressions as numbers in a numeric context, with 0 for false and 1 for true.
Similar method does the explicit division:
select 100*count(review) / count(*)
from reviews;
-- now for finished
-- Calculate the percentage with the formula
-- 100 * value / (total)
select _not_null, _null, percentage FROM (
select _not_null, _null, (100 * _not_null / ( _not_null + _null)) as percentage FROM (
-- it starts here !!!
-- sum 1 for each time the column in null
-- and 0 for each time the column is not null
select some_column,
sum( CASE WHEN review is not null THEN 1 ELSE 0 END) as _not_null,
sum( CASE WHEN review is null THEN 1 ELSE 0 END) as _null
from my_table
group by some_column
) as internal
) as internal2
--
-- you can select values over a percentage yet
--
where internal2.percentage > 75

MySQL Custom calculate views

I am having difficulty trying to combine a set of queries I am using. I currently am having to execute multiple queries with different years and CID numbers. (CID is 1-5, Years are CurrentYear to CurrentYear-6)
Current SQL Statement:
SELECT
1 as CID,
2015 as `Year`,
( SELECT SUM( legacy_reports_payments.AmountPaid ) * -1 AS Payments FROM legacy_reports_payments
WHERE DatePaid >= "2015-01-01 00:00:00" AND DatePaid < "2015-02-01 00:00:00"
AND CID = 1) As PaymentsJan,
( SELECT SUM( legacy_reports_payments.AmountPaid ) * -1 AS Payments FROM legacy_reports_payments
WHERE DatePaid >= "2015-02-01 00:00:00" AND DatePaid < "2015-03-01 00:00:00"
AND CID = 1) As PaymentsFeb,
( SELECT SUM( legacy_reports_payments.AmountPaid ) * -1 AS Payments FROM legacy_reports_payments
WHERE DatePaid >= "2015-03-01 00:00:00" AND DatePaid < "2015-04-01 00:00:00"
AND CID= 1) As PaymentsMar,
...
This returns:
CID | Year | PaymentsJan | PaymentsFeb | ...
1 | 2015 | 3000.00 | 3000.00 | ...
I want to create a single view that can show all of the calculated information, instead of running several queries like the one above.
I would like to have the view look more like this:
CID | Year | PaymentsJan | PaymentsFeb | ...
1 | 2015 | 3000.00 | 3000.00 | ...
2 | 2015 | 2000.00 | 2000.00 | ...
3 | 2015 | 5000.00 | 5000.00 | ...
1 | 2014 | 1000.00 | 3000.00 | ...
2 | 2014 | 3000.00 | 4000.00 | ...
3 | 2014 | 2000.00 | 5000.00 | ...
Can anyone provide any insight on the best way to handle this?
You could build a temporary table with the Years and CIDs you need and then join your query on that to do all of them at one. That many sub queries will be slow, but better than doing each one manually.
create table #Dates
(
controlCID int
,controlYear varchar(4)
)
DECLARE #year int = YEAR( getDate() )
DECLARE #numYears int = 6
DECLARE #numCID int = 5
DECLARE #CIDcounter int
while (#numYears > 0)
BEGIN
SET #CIDcounter = #numCID
while (#CIDcounter > 0)
BEGIN
insert into #Dates values ( #CIDcounter, (#year - #numYears + 1))
SET #CIDcounter = #CIDcounter - 1
END
SET #numYears = #numYears -1
END
select controlCID
,controlYear
--,( SELECT SUM( legacy_reports_payments.AmountPaid ) * -1 AS Payments FROM legacy_reports_payments
--WHERE p.DatePaid >= (controlYear+'-01-01 00:00:00') AND p.DatePaid < (controlYear+'-02-01 00:00:00')
--AND p.CID = controlCID) As PaymentsJan,
--etc and so on...
from #Dates
order by controlYear desc, controlCID
drop table #Dates
Just use the year and CID from the table in the sub queries and you should be good to go.

Join a table on itself

I have a table ValuationHistory with the following columns
Code | ValuationDate | NetAssetValue | PricePerShare | Subscriptions | Redemptions
ABC | 2014-06-30 | 12546.50 | 100.23 | 60 | 70
CEF | 2014-06-30 | 10025.20 | 120.50 | 30 | 20
ABC | 2014-07-31 | 12505.50 | 101.50 | 40 | 60
ABC | 2014-08-31 | 13051.41 | 102.50 | 35 | 70
Now the user will select a Valuation date and a code from an aspx page. I want to write an sql query that will give me a report with the following columns for all the valuation dates prior to the valuation date selected by the user
Code | BeginningEquity | Subscriptions | Redemptions | EndingEquity
Where
Code is the code selected by the user
BeginningEquity is the NetAssetValue for the previous valuation
Subscriptions is straight forward
Redemptions is straight forward
EndingEquity is the NetAssetValue on the valuationDate
I first created a table variable called #ValDates and got all the valuation dates into this temp table. Then I did a join of #ValDates with the ValuationHistory table.
But I am getting an error on the following query. The error message is:
subquery returned more than one value
Could somebody help me write this query in a better way
SELECT (SELECT NetAssetValue
FROM ValuationHistory
WHERE ValuationDate IN (SELECT Max(ValuationDate)
FROM ValuationHistory
WHERE ValuationDate < nd.ValuationDate)),
Subscriptions,
Redemptions,
EndingEquity
FROM ValuationHistory vh
INNER JOIN #ValDates vd
ON vh.ValuationDate = vd.ValuationDate
WHERE vh.Code = #Code
AND vh.ValuationDate < = #ValuationDate
I'm not very clear about your requirement. Based on the description, you may try to start your query as below
DECLARE #Code VARCHAR(10) = 'ABC'
,#ValuationDate date = '2014-07-31'
;WITH cte AS (
SELECT
rn = ROW_NUMBER() OVER (PARTITION BY Code ORDER BY ValuationDate), *
FROM ValuationHistory
)
SELECT
c1.Code,
c2.NetAssetValue AS BeginningEquity,
c2.ValuationDate AS BeginningDate,
c1.NetAssetValue AS EndingEquity,
c1.ValuationDate AS EndingDate,
c1.[Subscriptions],
c1.[Redemptions]
from cte c1
LEFT JOIN cte c2
ON c1.Code = c2.Code
AND c2.rn = c1.rn - 1
WHERE c1.Code = #Code
AND c1.ValuationDate < = #ValuationDate
SQL Fiddle Demo

how to show virtual column with calculated runtime values(i.e not in database) in mysql?

I've following table stuinfo
+------------+-------+-----------+
| Roll_no | Name | Lastname |
+------------+-------+-----------+
| 37032 | johny | bravo |
| 37034 | sam | smith |
+------------+-------+-----------+
and second one stu_attendace
+------------+-------+-----------+
| Roll_no | Name | month |
+------------+-------+-----------+
| -1 | total | 26 |
| 37032 | johny | 19 |
| 37034 | sam | 25 |
+------------+-------+-----------+
Total days are 26 , so johny's attendance is 73.03% and Sam's attendance is 95.03% .
So how can I show their Attendance with percentage by calculating at run-time and showing those values in new column 'per_attendace' which is actually not in database. like shown in below
+----------+--------+-----------+---------------+
| roll_no | name | last_name | per_attendace |
+----------+--------+-----------+---------------+
| 37032 | johny | bravo | xx % |
| 37034 | sam | smith | xx % |
+----------+--------+-----------+---------------+
You can easily do what you want, in the SELECT clause you can select a literal value, or a column name, or any valid expression.
Just JOIN the two tables, and then calculate the percent like this:
SELECT
i.roll_no,
i.name,
i.Lastname,
((a.month / 26) * 100) AS percent
FROM stuinfo AS i
INNER JOIN stu_attendance AS a ON i.Roll_no = a.Roll_no;
Note that:
There is no need to duplicate the column name in both the two tables, just remove it from the other table, and keep your tables normalized.
Don't store the total value like this as a value in the column.
Update:
If you want to select the value of the total from the database, you can do this:
SELECT
i.roll_no,
i.name,
i.Lastname,
((a.month / (SELECT month
FROM stu_attendace
WHERE name = 'total')) * 100) AS percent
FROM stuinfo AS i
INNER JOIN stu_attendance AS a ON i.Roll_no = a.Roll_no;
SQL Fiddle Demo
You can also set that total value as a variable instead of the correlated subquery, like this:
SET #total = 0;
SELECT month
FROM stu_attendace
WHERE name = 'total'
INTO #total;
Then:
SELECT
i.roll_no,
i.name,
i.Lastname,
ROUND(((a.month / #total) * 100), 2) AS percent
FROM stuinfo AS i
INNER JOIN stu_attendace AS a ON i.Roll_no = a.Roll_no;
I used the ROUND function to round the number to only two decimal values.
Updated SQL Fiddle Demo
Try this
select a.roll_no , a.name ,a.last_name,(b.month/26)*100 as per_attendace
from stuinfo as a join
stu_attendace as b on a.roll_no=b.roll_no
try
SELECT i.roll_no, i.name, i.Lastname, ((a.month / 26) * 100) AS percent
FROM stuinfo AS i
INNER JOIN stu_attendance AS a ON i.Roll_no = a.Roll_no;