Customer Dimension - sql

I am writing a SQL code to create a Customer Dimension.
ID Name File Import Date
1 XXX 12/30/2018
1 XXX 12/31/2018
1 XXX 1/1/2019
1 YYY 2/2/2019
1 YYY 3/2/2019
1 YYY 4/2/2019
2 AAA 1/1/2019
I want to create a Query where I can capture the distinct Name along with the History
New table
ID Name Active
1 XXX 0
1 YYY 1
2 AAA 1
Below query give me the latest record
SELECT Distinct a.[ID] as CustID
,a.[Name] as CustName
FROM X as a
inner join
(select ID,[MaxDate] = MAX(FileImportDate) from X group by ID ) b
on a.ID = b.ID
and a.FileImportDate = b.MaxDate`
enter code here`

I'll bite...
Going by comments, this is a guess
Example
Select Top 1 with ties
ID
,Name
,Active = case when [FileImportDate] = max([FileImportDate]) over (Partition By ID) then 1 else 0 end
From YourTable
Order By Row_Number() over (Partition By Name Order by [FileImportDate] Desc)
Returns
ID Name Active
2 AAA 1
1 XXX 0
1 YYY 1
Here is a dbFiddle

With distinct and case:
select
distinct t.id, t.name,
case
when exists (
select 1 from tablename
where
id = t.id
and name <> t.name
and fileimportdate > t.fileimportdate) then 0
else 1
end active
from tablename t
See the demo
Results:
id name active
1 XXX 0
1 YYY 1
2 AAA 1

Related

Update a table based on the count of specific records in the same table

I'm using Microsoft SQL Server and I have a table shown below in my database:
Id groupName flag type
--------------------------------
1 aaa 0 0
2 aaa 1 0
3 aaa 0 0
4 bbb 0 0
5 bbb 0 0
6 bbb 0 0
I'd like to update the type column values based on the count of specific records in the same table.
Assuming that the records are grouped by their groupName, if a group has at least 1 record whose flag = 1, the typevalues of all the records in the same group should be 1, else 0.
In my case, I'd like to get the following output.
Id groupName flag type
--------------------------------
1 aaa 0 1
2 aaa 1 1
3 aaa 0 1
4 bbb 0 0
5 bbb 0 0
6 bbb 0 0
How can I do this with a SQL statement?
Edit
The following, of course, does not work.
UPDATE t1
SET type = CASE
WHEN ((SELECT COUNT(*) FROM tg) > 0)
THEN 1
ELSE 0
END
FROM [sampleDB].[dbo].[groups] t1
JOIN (SELECT t2.groupName, t2.flag
FROM [sampleDB].[dbo].[groups] t2) tg ON tg.groupName = t1.groupName
AND tg.flag = 1;
You can try the below -
UPDATE t
SET
type = case when cnt>=1 then 1 else 0 end
FROM
tablename t
INNER JOIN (select group_name, count(case when flag=1 then 1 end) as cnt from tablename
group by group_name)t1
ON t.group_name= t1.group_name
I would use window functions:
with toupdate as (
select g.*, max(flag) over (partition by groupname) as new_flag
from [sampleDB].[dbo].[groups] g
)
update toupdate
set flag = new_flag
where flag <> new_flag;
Note the where clause so only rows where the flag is changing are updated. SQL Server attempts to update row even when no values change, incurring overhead. The where clause saves most of this overhead.
You can simply do it by using a simple JOIN as:
UPDATE D
SET Type = 1
FROM Data D JOIN
(
SELECT GroupName
FROM Data
WHERE Flag = 1
) T ON D.GroupName = T.GroupName;
Here is a db-fiddle
This is the most performant way to solve this problem by using exists statement:
update t1
set type = 1
from table t1
where exists ( select 1
from table t2
where t1.groupname = t2.groupname
and flag = 1
)
This could be other option
;with data
As (
Select count(groupname) groupname
,max(flag) flag
,type
From Tablename
Group by type
Having count(groupname)>1
And max(flag)=1
)
Update c
Set type =1
From data c

SQL update all records except the last one with a value

I need to make a query where only the last line of each user that has a car gets a license plate number.
ID UserId LicensePlate HasCar
1 1 ABC123 1
2 1 ABC123 1
3 2 NULL 0
4 3 UVW789 1
5 3 UVW789 1
Should become:
ID UserId LicensePlate HasCar
1 1 NULL 1
2 1 ABC123 1
3 2 NULL 0
4 3 NULL 1
5 3 UVW789 1
So I basically need to find all users with a licenseplate and change all but the last one and make the LicensePlate NULL
Assuming the ID column is an identity column so it can provide the ordering, something like this should do the trick:
;WITH CTE AS
(
SELECT Id,
UserId,
LicensePlate,
ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY Id DESC) rn
FROM Table
WHERE HasCar = 1
)
UPDATE CTE
SET LicensePlate = NULL
WHERE rn > 1
You can try this
UPDATE l
SET l.LicensePlate = null
FROM Car l
INNER JOIN (SELECT UserId, Max(Id) AS max_id
FROM Car
GROUP BY UserId) m ON m.UserId = l.UserId
AND m.max_id <> l.id
You can do it with a join on the table itself like that :
UPDATE car c
INNER JOIN car c2 ON c.userId = c2.userId AND c.id < c2.id AND c.HasCar = 1 AND c2.HasCar = 1
SET c.LicensePlate = NULL
The condition c.id < c2.id will avoid to select the last line
By using LAG Function also you can achieve it.
;WITH License(ID,UserId,LicensePlate,HasCar)
as
(
SELECT 1,1,'ABC123',1 UNION ALL
SELECT 2,1,'ABC123',1 UNION ALL
SELECT 3,2,NULL ,0 UNION ALL
SELECT 4,3,'UVW789',1 UNION ALL
SELECT 5,3,'UVW789',1
)
SELECT ID,UserId,LAG(LicensePlate,1,NULL) OVER(PARTITION BY UserId ORDER BY LicensePlate),HasCar FROM License

T-SQL group by 2 tables

My tables looks like this:
For each TimeOfDay I would like to get the most frequent Category. For example if there are 3 auctions with unique ClosedTime but each of this Time has TimeOfDay=1 and 2 of these auctions have CategoryId=1 and one auction CategoryId=2 I would like to get:
TimeOfDay | CategoryId
1 | 1
I have tried group by TimeOfDay and CategoryId but still I don't know how to get top category for each TimeOfDay group. I have this:
select t.TimeOfDay, a.CategoryId, count(a.CategoryId)
numberOfSalesInCategory
from Auction a
join Time t on t.Id = a.ClosedTime
where IsSuccess = 1
group by t.TimeOfDay, a.CategoryId
and result for some sample data:
TimeOfDay | CategoryId | numberOfSalesInCategory
0 1 1
1 1 1
1 2 3
2 2 1
0 3 1
3 3 1
3 4 2
So for these data I would like to get:
TimeOfDay | CategoryId
0 | 1 or 3 numberOfSalesInCategory for both is 1
1 | 2 numberOfSalesInCategory is 3
2 | 2 only one category
3 | 4 numberOfSalesInCategory is 2
Technically, you are looking for the mode. There can be multiple modes, if multiple values all have the same frequency. If you are happy to arbitrarily choose one, then a conditional aggregation with row_number() is the solution:
select TimeOfDay,
max(case when seqnum = 1 then CategoryId end) as ModeCategory
from (select t.TimeOfDay, a.CategoryId, count(*) as numberOfSalesInCategory,
row_number() over (partition by t.TimeOfDay order by count(*) ) as seqnum
from Auction a join
Time t
on t.id = a.ClosedTime
where a.isSuccess = 1
group by t.TimeOfDay, a.CategoryId
) ta
group by TimeOfDay;
You could put the current statement in a CTE, rank them with RANK() and then do a stuff statement.
e.g.
; WITH T AS (SELECT t.TimeOfDay, a.CategoryId, COUNT(a.CategoryId)
numberOfSalesInCategory
FROM Auction a
JOIN Time t ON t.Id = a.ClosedTime
WHERE IsSuccess = 1
GROUP BY t.TimeOfDay, a.CategoryId)
, S AS (SELECT T.*
, RANK() OVER (PARTITION BY TimeOfDay ORDER BY numberOfSalesInCategory DESC) RankOrder
FROM T)
SELECT DISTINCT TimeOfDay
, STUFF(((SELECT ' or ' + CONVERT(NVARCHAR, CategoryId)
FROM S
WHERE RankOrder = 1
AND TimeOfDay = BloobleBlah.TimeOfDay
FOR XML PATH('')), 1, 4, '') CategoryId
FROM S BloobleBlah

Select Grouped Column Values Where Have Same Id In SQL Server

I have a table like this.
TABLE-1
id Code
-----------------
1 N188
1 N1Z2
1 N222
2 N189
2 N1Z2
2 N1Z3
3 N188
3 A123
3 B321
4 N188
4 A333
4 B444
I want to select id and code only code has N188.Result should like this:
TABLE-2
id Code
---------------
1 N188
1 N1Z2
1 N222
3 N188
3 A123
3 B321
4 N188
4 A333
4 B444
How can I write sql for this in SQL Server?
Thanks
You can use EXISTS for this:
SELECT id, code
FROM table1 t
WHERE EXISTS (
SELECT 1
FROM table1 t2
WHERE t.id = t2.id
AND t2.Code = 'N188'
)
Condensed SQL Fiddle Demo
Using INNER JOIN
SELECT *
FROM tablename A
JOIN (SELECT id
FROM tablename
WHERE code = 'N188') B
ON a.id = b.id
Here is an alternative method that uses window functions:
select id, code
from (select t.*,
sum(case when code = 'N188' then 1 else 0 end) over (partition by id) as cnt_n188
from table t
) t
where cnt_n188 > 0;

Set value of column to 0 for only one of the duplicate records?

I have the following table, and I want to set the value of deleted column to zero for only one of the records that have duplicate names.
id name deleted
------------------------
1 a 1
2 a 1
3 a 1
4 b 1
5 c 1
6 d 1
so the output will be:
id name deleted
------------------------
1 a 0
2 a 1
3 a 1
4 b 0
5 c 0
6 d 0
If your dbms is SQL-Server( >= 2005) you can use a CTE with ROW_NUMBER:
WITH CTE AS
(
SELECT ID, Name, Deleted,
RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY ID)
FROM dbo.T
)
UPDATE CTE
SET Deleted = 0
WHERE RN = 1
DEMO
UPDATE Tbl SET deleted = 0 WHERE id IN
(SELECT MIN(id) FROM Tbl GROUP BY name)
UPDATE A
SET A.deleted = 0
FROM Tbl A
LEFT JOIN Tbl B
ON A.Name = B.Name
AND A.Id > B.Id
WHERE B.Id IS NULL