I have a problem that I could easily solve if I had window functions available in Sybase, but I dont:
Consider a table test:
+------------+----------------+-------------+
| Account_Id | Transaction_Id | CaptureDate |
+------------+----------------+-------------+
| 1 | 1 | 2014-01-01 |
| 1 | 2 | 2013-12-31 |
| 1 | 3 | 2015-07-20 |
| 2 | 1 | 2012-02-20 |
| 2 | 2 | 2010-01-10 |
| ... | ... | ... |
+------------+----------------+-------------+
I want to get a result set containing for each Account The most recent CaptureDate with the corresponding Transaction_Id. With the window function row_number this would be easy:
select Accounts_Id, CaptureDate, Transaction_Id from
(select
CallAccounts_Id,
CaptureDate,
Transaction_Id,
ROW_NUMBER() OVER(partition by Accounts_Id order by CaptureDate desc) row
from test) tbl
where tbl.row = 1
but my sybase version does not have this. Obviously, sth like
select max(Transaction_Id ), max(Transaction_Id ), Account_Id
from test
group by Account_Id
does not work because it does not always give me the correct Transaction_Id.
How can I do this then in Sybase and not make it terribly verbose?
Thanks!
Try below:
SELECT Account_Id, Transaction_Id, CaptureDate
FROM test a
WHERE CaptureDate = (
SELECT MAX(CaptureDate)
FROM test b
WHERE a.Account_Id = b.Account_Id
)
EDIT 1:
Duplicate CaptureDate was not in your example, so I did not take care of that scenario. Try below:
SELECT Account_Id, Transaction_Id, CaptureDate
FROM test a
WHERE CaptureDate = (
SELECT MAX(CaptureDate)
FROM test b
WHERE a.Account_Id = b.Account_Id
)
AND Transaction_Id =
(
SELECT MAX(Transaction_Id)
FROM test c
WHERE a.Account_Id = c.Account_Id
AND a.CaptureDate = c.CaptureDate
)
Related
This is SQL Server 2016. I have the following data in only one table:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+--------+-------------+-----------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 0
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 0
I want to group by custID, prodID and title and update isMasterTitle field to 1 for every max() titleCount per group.
So, I want the following:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+----------+------------+---------------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 1
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 1
I'm trying the following:
UPDATE [dbo].[_Variations]
SET isMasterTitle = 1
FROM [dbo].[_Variations] v1
INNER JOIN (SELECT custID, prodID, MAX(titleCount) AS mtitleCount
FROM [_Variations]
GROUP BY custID,prodID) as v2 ON v1.custID = v2.custID and v1.prodID = v2.prodID and v1.titleCount = v2.mtitleCount
try the following:
;with cte
as
(
select isMasterTitle, ROW_NUMBER() over (partition by custID, prodID order by titleCount desc) rn
from #t
)
update cte
set isMasterTitle = 1
where rn = 1
select * from #t
Your given code also works fine.
Please find the db<>fiddle here.
I would recommend leveraging a powerful feature of SQL Server called the updateable common-table-expression.
You can build a cte that uses window functions to identify which row should be updated, and then directly update it; there is no need to join again the original table in the outer query. This makes the query both shorter and more efficient:
with cte as (
select
isMaster,
row_number() over(partition by custID, prodID order by titleCount desc) rn
from [dbo].[_Variations]
)
update cte set isMaster = 1 where rn = 1
Let's say I have a table with many columns like col1, col2, col3, id, variantId, col4, col5 etc
However I am only interested in id, variantId which look like this:
+----------+-----------+
| id | variantId |
+----------+-----------+
| a | 11 |
| a | 12 |
| b | 31 |
| c | 41 |
| c | 54 |
| d | abc |
| e | xyz |
| e | xyz |
+----------+-----------+
I need distinct ids which having count of distinct variantId more than once
In this case I would only get a and c
You can use group by and having:
select id
from t
group by id
having min(variant_id) <> max(variant_id);
You can also use:
having count(distinct variant_id) > 1
Try with group by having clause
select id
from table
group by id
having count(distinct variant_id) > 1
You can do it more efficiently with EXISTS:
select distinct t.id
from tablename t
where exists (
select 1 from tablename
where id = t.id and variantid <> t.variantid
)
I am working on a poorly designed database. I need to query the table to find the solution for the following issue.
Let's say I have a table like this.
+------------+------------+--------------+-----------+
| id | SubCode | Type | NumId |
+----------------------------------------------------+
| 1 | SB1212 | TCH | 100000000 |
| 1 | SB1212 | APP | 100000000 |
| 1 | SB1212 | TCH | 100000001 |
| 1 | SB1212 | APP | 100000002 |
+----------------------------------------------------+
I need to find the NumId of the people who only belong to Type='TCH' ( They shouldn't belong to Type='APP').
Note that the NumId is can be duplicated.
I wrote the following query and it's taking too much time to load. This looks like a simple issue but I am working on this for too long to not see a solution now. Can someone point me where I am doing wrong?
SELECT NumId
FROM TeacherSubject
WHERE SubCode = 'SB1212'
AND Type = 'TCH' OR Type = 'APP'
AND id NOT IN (SELECT NumId FROM TeacherSubject
WHERE SubCode = 'SB1212'
AND Type = 'APP')
ORDER BY NumId DESC
The output I am expecting is 100000001. Because 100000000 belongs to TCH and APP types.
You could use a CTE
with cte as (
select *
from TeacherSubject
where [Type] = 'APP'
and Subcode = 'SB1212'
)
select NumID
from TeacherSubject
where [Type] = 'TCH'
and Subcode = 'SB1212'
and NumID not in (select NumID from cte)
order by NumID desc
Try this
SELECT
NumId
FROM YourTable YT
WHERE [Type] = 'TCH'
AND SubCode = 'SB1212'
AND NOT EXISTS
(
SELECT
1
FROM YourTable
WHERE NumId = YT.NumId
AND [Type] <> YT.[Type]
AND [SubCode] = YT.SubCode
)
Let's say we have this and want to see all Tasks, that havent been done yet and an additional column showing how many open Tasks there are left for this customer.
I have a table like this in my database:
+------------+--------------------------+-------+
| CustomerID | Task | Done |
+------------+--------------------------+-------+
| 1 | CleanRoom | False |
| 1 | Cleandishes | True |
| 1 | WashClothes | False |
| 2 | TakeDogsOut | True |
| 2 | PlayWithKids | True |
| 3 | HaveFunWithMrSamplesWife | True |
| 3 | CleanMrSamplesCar | False |
+------------+--------------------------+-------+
I need this as returned table:
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 2 |
| 1 | WashClothes | 2 |
| 3 | CleanMrSamplesCar | 1 |
+------------+-------------------+-------------+
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I know I could go like
SELECT
a.CustomerID,
a.Task,
(
Select count(*) from myTable where
customerID = a.CustomerID and
done = False
) as DoneOverAll
FROM myTable as a
WHERE Done = False
But I think that this is very ineffective, since it would execute a Select Count for each row in my table. Is there a way to achieve this with a JOIN using groupBy or something? I'm not into GroupBy commands yet.
Okay I should have tried first. Came up with the following;
Select count(*), CustomerID from myTable group by CustomerID
All I need to do now is to get this into a join.
Okay, got it. Sorry again for not trying first!
SELECT
a.CustomerID,
a.Task,
b.cnt
FROM myTable as a
LEFT JOIN (select count(*) AS cnt, CustomerID FROM myTable GROUP BY CustomerID) as b on a.CustomerID = B.CustomerID
WHERE Done = False
Question left;
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I'm not sure why Done = False, but this is your logic. :-)
Here's what I would do, without the LEFT JOIN.
SELECT
a.CustomerID,
a.Task,
SUM(CASE WHEN a.Done = 'False' THEN 1 ELSE 0 END) DoneOverAll,
SUM(Case WHEN a.Done = 'True' THEN 1 ELSE 0 END) NotDone
FROM myTable as a
Group By a.CustomerID, a.Task
Do calculate separately .
;with tempfalse as(
SELECT
a.CustomerID,
a.Task,
count(*) as DoneOverAll
FROM myTable as a
WHERE Done = False
group by a.CustomerID, a.Task
)
, temptrue (
SELECT
a.CustomerID,
a.Task,
count(*) as total
FROM myTable as a
group by a.CustomerID, a.Task
)
SELECT
a.CustomerID,
a.Task,
cast(NULLIF(DoneOverAll,0) as varchar (10) ) + '/' + cast(NULLIF(b.total,0) as varchar (10) )
from temptrue as a left join tempfalse b
on a.CustomerID =a.CustomerID and
a.Task = b.Task
I have following table:
ID | source | Name | Age | ... | ...
1 | SQL | John | 18 | ... | ...
2 | SAP | Mike | 21 | ... | ...
2 | SQL | Mike | 20 | ... | ...
3 | SAP | Jill | 25 | ... | ...
I want to have one record for each ID. The idea behind this is that if the ID comes only once (no matter the Source), that record will be taken. But, If there are 2 records for one ID, the one containing SQL as source will be the used record here.
So, In this case, the result will be:
ID | source | Name | Age | ... | ...
1 | SQL | John | 18 | ... | ...
2 | SQL | Mike | 20 | ... | ...
3 | SAP | Jill | 25 | ... | ...
I did this with a partition over (ordered by Source desc), but that wouldn't work well if a third source will be added one day.
Any other options/ideas?
The easiest approach(in my opinion) is using a CTE with a ranking function:
with cte as
(
select ID, source, Name, Age, ... ,
rn = row_number() over (partition by ID order by case when source = 'sql'
then 0 else 1 end asc)
from dbo.tablename
)
select ID, source, Name, Age, ...
from cte
where rn = 1
You can use ROW_NUMBER:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER( PARTITION BY ID
ORDER BY CASE WHEN [Source] = 'SQL' THEN 1 ELSE 2 END)
FROM dbo.YourTable
)
SELECT *
FROM CTE
WHERE RN = 1;
You can use the WITH TIES clause and the window function Row_Number()
Select Top 1 With Ties *
From YourTable
Order By Row_Number() over (Partition By ID Order By Case When Source = 'SQL' Then 0 Else 1 End)
How about
SELECT *
FROM table
WHERE ID in (
SELECT ID FROM test
group by ID
having count(ID) = 1)
OR source = 'SQL'