How can I get the column value from the most recent date? - sybase-ase

Running this on Sybase ASE.
I get an error Subquery returned more than 1 value. This is illegal when the subquery follow =, etc.
In the subquery I'm trying to select the latest value by date.
Data would be something like
Doc_no user Date
AA jones 23 Mar 2018
AA steph 10 Mar 2018
BB dan 1 Jan 2018
BB jon 1 Jan 2010
In this example, I should get 2 values returned to join on and display.
AA should show jones
BB should show dan since they have the latest date.
The data is actually millions of rows to search through.
use mydb_hist
go
select
d.doc_no,
d.doc_short_name,
d.doc_name,
r.doc_no,
r.revision_code,
r.revision,
r.description,
d.last_update,
d.document_category_code,
d.business_group_code,
(case
when substring(grp.group_name,1,10) = 'GEL IPP Do' THEN 'Yes'
else 'No'
END) as 'GEL',
(SELECT d2.index_user_id
FROM document_instance d2
WHERE d2.doc_no = d1.doc_no
and d2.entry_time = (select max(d3.entry_time)
from document_instance d3
where d3.doc_no = d1.doc_no)
) as 'last indexed by'
from documents d
join document_revision r on r.doc_no = d.doc_no
left join mydb..document_type_group grp on grp.doc_no = d.doc_no and substring(grp.group_name,1,10) = 'Leg PII Do'
left join mydb..document_instance d1 on d1.doc_no = d.doc_no
where d.dbOprnCD = 'I'
and d.last_update > '1 Jan 2018'
--and grp.group_name LIKE 'GEL%' --shortened this so the substring in the group by matches, getting a server error for the work table size otherwise
group by d.doc_no,
d.doc_short_name,
d.doc_name,
r.doc_no,
r.revision_code,
r.revision,
r.description,
d.last_update,
d.document_category_code,
d.business_group_code,
substring(grp.group_name,1,10)

How about this?
If index_user_id is not unique for a given doc_no and entry_date then you need to move the subselect to the join below. This will then allow you to include index_user_id in the group by clause.
use mydb_hist
go
select
d.doc_no,
d.doc_short_name,
d.doc_name,
r.doc_no,
r.revision_code,
r.revision,
r.description,
d.last_update,
d.document_category_code,
d.business_group_code,
(case
when substring(grp.group_name,1,10) = 'GEL IPP Do' THEN 'Yes'
else 'No'
END) as 'GEL',
d1.index_user_id as 'last indexed by'
from documents d
join document_revision r on r.doc_no = d.doc_no
left join mydb..document_type_group grp on grp.doc_no = d.doc_no and substring(grp.group_name,1,10) = 'Leg PII Do'
left join mydb..document_instance d1 on d1.doc_no = d.doc_no and d1.entry_time = (select max(d3.entry_time)
from mydb..document_instance d3
where d3.doc_no = d1.doc_no)
where d.dbOprnCD = 'I'
and d.last_update > '1 Jan 2018'
group by d.doc_no,
d.doc_short_name,
d.doc_name,
r.doc_no,
r.revision_code,
r.revision,
r.description,
d.last_update,
d.document_category_code,
d.business_group_code,
substring(grp.group_name,1,10)
d1.index_user_id

Related

Return value mysql if not exist for one condition

I want to return the value even if value not exist. For example the user with id = 245 has value only for year 2017, but the idea is to return value 0 for year 2016 and value = 0 for year = 2018 if no data in database.
SELECT member_id, year,
SUM(amount_nitrogen) as amount_nitrogen,
SUM(amount_potassium) as amount_potassium,
SUM(amount_phosphorus) as amount_phosphorus,
SUM(amount_sodium_oxide) as amount_sodium_oxide
FROM ct
WHERE year >= 2016 and year <= 2018
GROUP BY member_id, year
If the user has data for all 3 years : 2018,2017,2016 it's good, but if the user has only for one year, I get data only for this year. The scope is to get data for others 2 years and this data shoul be 0
Use a CROSS JOIN to generate the rows and LEFT JOIN to bring in the data:
SELECT m.member_id, y.year,
COALESCE(SUM(ct.amount_nitrogen), 0) as amount_nitrogen,
COALESCE(SUM(ct.amount_potassium), 0) as amount_potassium,
COALESCE(SUM(ct.amount_phosphorus), 0) as amount_phosphorus,
COALESCE(SUM(ct.amount_sodium_oxide), 0) as amount_sodium_oxide
FROM (SELECT DISTINCT member_id FROM ct) m CROSS JOIN
(SELECT 2016 as year UNION ALL SELECT 2017 UNION ALL SELECT 2018
) y LEFT JOIN
ct
ON ct.member_id = m.member_id AND ct.year = y.year
GROUP BY m.member_id, y.year

Get rows that differ

I have a table properties:
propertyID userID defaultStateID defaultStateYear
1 1 1 2017
2 1 2 2017
3 2 1 2018
4 3 3 2018
Each property belongs to certain user (user 1 has properties 1, 2; user 2 has property 3, user 3 has property 4). Each property has a default state identified by defaultStateID and defaultStateYear (referring to table states below).
Then I have a table states showing data about properties at certain points of time (data about them - owner and area - change from time to time):
stateID propertyID owner area timestamp stateYear
1 1 David Smith 15 123456789 2017
2 2 Amanda Green 74 123456799 2017
3 1 David Smith 15 123456999 2017
1 3 Amanda Green 12 123459999 2018
2 1 Peter Black 11 123499999 2018
3 4 Jack Stone 62 123999999 2018
4 2 Amanda Green 70 129999999 2018
Combination of stateID and stateYear is unique in this table.
In the example above, state of propertyID 1 changed from its default state (stateID 1 in year 2017) compared with the most recent state (stateID 2 in year 2018). Similarly, state of propertyID 2 changed.
How to show changes for certain user in his properties (using MySQL)?
Desired output (for userID 1):
propertyID owner area timestamp
1 David Smith 15 123456789
1 Peter Black 11 123499999
2 Amanda Green 74 123456799
2 Amanda Green 70 129999999
I tried the following piece of code but the code is just loading for unlimited time (in phpMyAdmin) giving no output/error:
SELECT p.propertyID, -- getting default state of property
p.defaultStateID,
p.defaultStateYear,
s.owner AS owner,
s.area AS area,
s.timestamp AS timestamp
FROM properties AS p
LEFT JOIN states AS s
ON p.propertyID = s.propertyID
AND p.defaultStateID =
(SELECT si.stateID
FROM states AS si
WHERE si.stateID = p.defaultStateID AND si.stateYear = p.defaultStateYear AND p.userID=1)
UNION ALL
SELECT p.propertyID, -- getting the most recent state of property
p.defaultStateID,
p.defaultStateYear,
s.owner AS owner,
s.area AS area,
s.timestamp AS timestamp
FROM properties AS p
LEFT JOIN states_full AS s
ON p.property_id = s.property_id
AND s.timestamp =
(SELECT MAX(timestamp)
FROM states
WHERE s.propertyID = p.propertyID)
WHERE p.userID=1
So, if I understand correctly this query here should do the trick for you :
SELECT s.propertyID, s.owner, s.area, s.timestamp
FROM states s
INNER JOIN properties p ON p.propertyID = s.propertyID
WHERE p.defaultStateID = s.stateID
AND p.userID = 1
First, we're selecting out columns that we need.
Second, we're doing an "INNER JOIN" so we can get only the data that
match our property.
After that we're filtering for the default state to be equal to the
stateID, and because you specified the desired output for user 1, I
have added a filter for userID matching the first user.
Hope that this is helpful for you.
You seem to want the initial and last states for a given user's properties.
However, your data model is very confusing. Why would something called stateid be duplicated in a table called states? Why would the join between the tables be on property and year? These really don't make sense. The database is not making proper use of keys on tables.
But, based on your description, this would seem to do what you want:
select s.*
from properties p join
(select s2.propertyid, max(s2.year) as maxyear
from states s2
group by s2.propertyid
) sp
on sp.propertyid = p.propertyid join
states s
on s.propertyid = p.propertyid and
s.year in (p.year, sp.maxyear)
order by sp.propertyid, sp.year;
EDIT:
This appears to do what you want:
select s.*
from properties p join
(select s2.propertyID, max(s2.timestamp) as maxtimestamp
from states s2
group by s2.propertyID
) sp
on sp.propertyID = p.propertyID join
states s
on s.propertyID = p.propertyID and
(s.timestamp = sp.maxtimestamp or
s.stateId = p.defaultStateID
)
where p.userID = 1
order by sp.propertyID;
Here is the SQL Fiddle.

SQL Server 2005 : find customers that doesn't order from one year

I'm trying to find customers names in a SQL Server 2005 database that don't have any orders, or ask for quotation from one year. I'm trying this but it doesn't work, because it show me only documents older than one year, but show them also if in the current year other documents of the same customers are presents.
SELECT DISTINCT
ANAGRAFICACF.CODCONTO,
DSCCONTO1,
'€ '+LEFT(TOTDOCUMENTO,LEN(TOTDOCUMENTO)-2),
CONVERT(VARCHAR(11),DATADOC,6),
TESTEDOCUMENTI.TIPODOC,
ANAGRAFICACF.PARTITAIVA,
ANAGRAFICACF.CODFISCALE,
ANAGRAFICARISERVATICF.NOTE1,
TESTEDOCUMENTI.DATADOC,
TESTEDOCUMENTI.CODAGENTE1,
ANAGRAFICAAGENTI.DSCAGENTE
FROM
dbo.TESTEDOCUMENTI
INNER JOIN
dbo.ANAGRAFICACF ON CODCLIFOR = CODCONTO
INNER JOIN
dbo.RIGHEDOCUMENTI ON PROGRESSIVO = IDTESTA AND TOTNETTORIGA <> '0'
INNER JOIN
dbo.ANAGRAFICARISERVATICF ON CODCLIFOR = ANAGRAFICARISERVATICF.CODCONTO
LEFT JOIN
dbo.ANAGRAFICAAGENTI ON CODAGENTE = TESTEDOCUMENTI.CODAGENTE1
WHERE
(dbo.TESTEDOCUMENTI.TIPODOC = 'ORDER' OR
dbo.TESTEDOCUMENTI.TIPODOC = 'QUOTATION')
AND (dbo.TESTEDOCUMENTI.DATADOC < DATEADD(DAY, -365, GETDATE())
ORDER BY
TESTEDOCUMENTI.DATADOC DESC
This is
CODCONTO | DSCCONTO1 | TOTDOCUMENTO| DATADOC | TIPODOC| CODAGENTE| DSCAGENTE|
---------+-----------+-------------+---------+--------+----------+----------+
C 10395 |CAIO|€ 1048.22 |03 Aug 15 |ORC |2015-08-03 00:00:00.000 |A 4 |F
C 51282 |SEMPRONIO|€ 2217.41 |03 Aug 15 |PCL |2015-08-03 00:00:00.000 |NULL
C 10120 |PINCO |€ 689.54 |03 Aug 15 |PCL |2015-08-03 00:00:00.000 |A 4 |F
C 746 |TIZIO |€ 205.45 |03 Aug 15 |ORC |2015-08-03 00:00:00.000 |A 4 |F
SOLUTION (thank you to #scsimon)
Add this where condition, with the NOT IN:
WHERE dbo.TESTEDOCUMENTI.TIPODOC = 'FVC' AND dbo.TESTEDOCUMENTI.DATADOC < DATEADD(DAY, -365, GETDATE())
AND TESTEDOCUMENTI.CODCLIFOR NOT IN (SELECT DISTINCT TESTEDOCUMENTI.CODCLIFOR
FROM dbo.TESTEDOCUMENTI
WHERE dbo.TESTEDOCUMENTI.TIPODOC = 'FVC' AND dbo.TESTEDOCUMENTI.DATADOC > DATEADD(DAY, -365, GETDATE())
)
Hope this will help, if someone else will have my request.
You want to use NOT EXISTS. If ANAGRAFICACF.CODCONTO is your CustomerNumber then the below should work for you. Since your column names aren't very self-explanatory, change them to what you need if I am incorrect. But, the logic of NOT EXISTS is what you want.
REMOVE:
AND NOT dbo.TESTEDOCUMENTI.DATADOC BETWEEN DATEADD(DAY, -365, GETDATE() ) AND GETDATE())
ADD:
AND ANAGRAFICACF.CODCONTO NOT IN
(SELECT DISTINCTANAGRAFICACF.CODCONTO
FROM dbo.ANAGRAFICACF
INNER JOIN
dbo.TESTEDOCUMENTI ON CODCLIFOR = CODCONTO
WHERE
dbo.TESTEDOCUMENTI.DATADOC > DATEADD(DAY, -365, GETDATE()))
SAMPLE DATA
Click HERE to run this code
IF OBJECT_ID('tempdb..#agents') IS NOT NULL DROP TABLE #agents
IF OBJECT_ID('tempdb..#items') IS NOT NULL DROP TABLE #items
create table #agents (AgentID varchar(2), name varchar(50))
insert into #agents values
('A1','Julius Cesar'),
('B2','Albert Einstien'),
('C3','Frank Thomas')
create table #items (AgentID varchar(2), ItemID int, ItemName varchar(50), DT datetime)
insert into #items (AgentID, ItemID, ItemName, DT) values
--Notice Juliues Cesar has items in the current year, AND older than a year
('A1',1,'Apple','7/1/2015'),
('A1',2,'Pear','7/1/2016'),
('A1',3,'Watermelon','12/1/2015'),
('A1',4,'Grape','1/1/2015'),
--Notice Albert Einstien only has items older than a year
('B2',5,'Car','7/1/2015'),
('B2',6,'Truck','5/1/2015'),
('B2',7,'Van','3/1/2015'),
--Notice Frank Thomas only has items older than a year
('C3',8,'Car','7/1/2014'),
('C3',9,'Truck','5/1/2014'),
('C3',10,'Van','3/1/2014')
Select
a.name
,i.*
from
#items i
inner join #agents a on
a.AgentID = i.AgentID
where
--Items that are older than a year
DT < DATEADD(DAY, -365, GETDATE())
AND i.AgentID NOT IN (select distinct AgentID from #items where dt > DATEADD(DAY, -365, GETDATE()))

show max row result sql with 2 'where' conditions

i have table master_pangkat
id_pangkat name
-----------------
a1 |Chef
a2 |Cleaning Service
a3 |Cashier
and i have table peg_pangkat
id nip id_pangkat year
----------------------
1 2001 a2 2012
2 2001 a3 2015
3 2012 a1 2016
4 2012 a1 2016
i want to show max id from table peg_pangkat where nip = 2001
i've tried this code
select * from peg_pangkat join master_pangkat on
peg_pangkat.id_pangkat=master_pangkat.id
where peg_pangkat.id=(select max(peg_pangkat.id) from peg_pangkat) ;
but it's show
4 2012 a1 2016
then when i add another where condition where nip=2001
it showed no result
select * from peg_pangkat join master_pangkat on
peg_pangkat.id_pangkat=master_pangkat.id
where nip=2001
and peg_pangkat.id=(select max(peg_pangkat.id) from peg_pangkat) ;
and the result i wanna show is
2 2001 a3 2015
You need to add a WHERE clause in the subquery.
SELECT *
FROM peg_pangkat pp
INNER JOIN master_pangkat mp
ON pp.id_pangkat = mp.id
WHERE
pp.nip = 2001
AND pp.id = (SELECT MAX(id) FROM peg_pangkat WHERE nip = pp.nip);
As a side note, you should use meaningful aliases on your tables to improve readability and maintainability.
Here is another method:
select *
from peg_pangkat p join
master_pangkat m
on p.id_pangkat = m.id
where p.nip = 2001
order by p.id desc
limit 1;

PHP/mysql checking all dates are accounted for

I have an app where I want the users to enter a job history. I store the results in a mysql table using the DATE format (start_date and end_date for each job). So for instance, after a couple entries it could look like this (job_history table:
Job Start End
Dishwasher 2013-07-10 2014-03-28
Banker 2014-04-02 2014-10-10
Cahsier 2014-10-11 2014-12-20
I'm trying to set up a mysql query that will check to see if there are any missing days from a certain year. For our example, lets use 2014. There are missing days from 2014-03-29 to 2014-04-01.
I haven't been able to find any good examples by searching. I figured there would be a better way to do it than doing a PHP loop and doing 365 queries (ouch!). Thanks
Edit: Updated example to incorporate year beginning and year end:
Job Start End
Dishwasher 2013-07-10 2013-12-01
Banker 2014-01-10 2014-10-10
Cahsier 2014-10-11 2014-12-20
In this example our input year would be 2014 and we would have 9 days at the beginning of the year that are uncovered, and 11 at the end of the year.
For those coming to this question later, the original answer was this
SELECT jh.id,
CASE
WHEN datediff(jh1.start, jh.end) > 1
THEN datediff(jh1.start, jh.end)
ELSE NULL
END as DifferenceInDays
FROM job_history jh
LEFT JOIN job_history jh1 ON jh1.id = (jh.id + 1)
what this does is it assumes a sequential id to self join to, that way you can compare the last rows date with the current rows date.
After a lot of additions to the requirements this will do what you want.
SELECT position, id, DifferenceInDays
FROM
( SELECT 'BEGINNING' as position, id,
#a := if(#a = 'BAD', 'BAD', if(DATE(concat(year(now()), '-01-01')) BETWEEN start AND end, 'BAD', '')),
case
WHEN #a = 'BAD'
THEN NULL
ELSE datediff(jstart, DATE(concat(year(now()), '-01-01')))
end AS DifferenceInDays
FROM
( SELECT jh.*, jh3.start as jstart
FROM job_history jh
CROSS JOIN(SELECT #a:='')t
LEFT JOIN (SELECT min(start) as start FROM job_history where userid = 123456 AND YEAR(start) = YEAR(NOW())) jh3 ON jh3.start = jh.start
WHERE jh.userid = 123456
ORDER BY id
)t
UNION ALL
SELECT 'MIDDLE', jh.id, '',
case
WHEN datediff(jh1.start, jh.end) > 1
THEN datediff(jh1.start, jh.end) - 1
ELSE NULL
end as DifferenceInDays
FROM
( SELECT *, #new_id := #new_id + 1 as new_id
FROM job_history, (select #new_id := 0) temp
WHERE userid = 123456
) jh
LEFT JOIN
( SELECT *, #new_id1 := #new_id1 + 1 as new_id
FROM job_history, (select #new_id1 := 0) temp
WHERE userid = 123456
) jh1 ON jh1.new_id = (jh.new_id + 1)
UNION ALL
SELECT 'ENDING', id,
#b := if(#b = 'BAD', 'BAD', if(DATE(concat(year(now()), '-12-31')) BETWEEN start AND end, 'BAD', '')),
case
WHEN #b = 'BAD'
THEN NULL
ELSE datediff(DATE(concat(year(now()), '-12-31')), jend)
end
FROM
( SELECT jh.*, jh2.end as jend
FROM job_history jh
CROSS JOIN(SELECT #b:='')t
LEFT JOIN (SELECT max(end) as end FROM job_history WHERE userid = 123456) jh2 ON jh2.end = jh.end
WHERE jh.userid = 123456
ORDER BY id
)t
ORDER BY case position WHEN 'BEGINNING' THEN 1 WHEN 'MIDDLE' THEN 2 ELSE 3 END ASC, id ASC
) t
WHERE DifferenceInDays IS NOT NULL
FIDDLE WITH MULTIPLE ROWS
FIDDLE WITH ONLY MIDDLE AND END
FIDDLE WITH NO MIDDLE ONE ROW
FIDDLE WITH ALL THREE
FIDDLE WITH NO END
You can date_diff function for finding difference between two date
$interval = date_diff($datetime1, $datetime2);

Resources