Piviot data in two tables? - sql

I have two tables as follows:
Table 1
Columns - oppproductid, SKU, Price, Quantity, Date
Values - PR1, ABCSKU1, 1000,500, 10/2013
Table 2
Columns - opproductid, month_1, Month_2, Month_3, Month_4...Month_36
Values - PR1, 200, 100, NULL, 200...
The tables are 1-1. I need to get one row for each value in the month column that is not null for each record and calculate the date based on the months that are not null assuming that Month_1 is the date column in the primary table so the ideal result set based on the sample values is:
oppproductid SKU Price Quantity Date Deployment
PR1 ABCSKU1 1000 500 10/2013 200
PR1 ABCSKU1 1000 500 11/2013 100
PR1 ABCSKU1 1000 500 1/2014 200
NOTES:
Month_3 is NULL so 12/2013 does not yield results.
There are 36 months in the second table with the only requirement that one has to contain data.
Month_1 always equals the date on the first table.
Any help is appreciated.

Store your data using the proper data types. Dates should be date fields.
Normalise your data structures to make querying easier.
Try this
.
set dateformat dmy
select
t1.oppproductid,
t1.SKU,
t1.Price,
t1.Quantity,
dateadd(month, monthno-1, convert(date, '1/' + [date])),
deployment
from table1 t1
inner join
(
select *, convert(int,substring(mth,7,2)) as monthno from table2
unpivot (deployment for mth in (month_1,month_2,month_3,month_4...)) u
) u2
on t1.oppproductid = u2.opproductid

Related

The nearest row in the other table

One table is a sample of users and their purchases.
Structure:
Email | NAME | TRAN_DATETIME (Varchar)
So we have customer email + FirstName&LastName + Date of transaction
and the second table that comes from second system contains all users, they sensitive data and when they got registered in our system.
Simplified Structure:
Email | InstertDate (varchar)
My task is to count minutes difference between the rows insterted from sale(first table)and the rows with users and their sensitive data.
The issue is that second table contain many rows and I want to find the nearest in time row that was inserted in 2nd table, because sometimes it may be a few minutes difeerence(delay or opposite of delay)and sometimes it can be a few days.
So for x email I have row in 1st table:
E_MAIL NAME TRAN_DATETIME
p****#****.eu xxx xxx 2021-10-04 00:03:09.0000000
But then I have 3 rows and the lastest is the one I want to count difference
Email InstertDate
p****#****.eu 2021-05-20 19:12:07
p****#****.eu 2021-05-20 19:18:48
p****#****.eu 2021-10-03 18:32:30 <--
I wrote that some query, but I have no idea how to match nearest row in the 2nd table
SELECT DISTINCT TOP (100)
,a.[E_MAIL]
,a.[NAME]
,a.[TRAN_DATETIME]
,CASE WHEN b.EMAIL IS NOT NULL THEN 'YES' ELSE 'NO' END AS 'EXISTS'
,(ABS(CONVERT(INT, CONVERT(Datetime,LEFT(a.[TRAN_DATETIME],10),120))) - CONVERT(INT, CONVERT(Datetime,LEFT(b.[INSERTDATE],10),120))) as 'DateAccuracy'
FROM [crm].[SalesSampleTable] a
left join [crm].[SensitiveTable] b on a.[E_MAIL]) = b.[EMAIL]
Totally untested: I'd need sample data and database the area of suspect is the casting of dates and the datemath.... since I dont' know what RDBMS and version this is.. consider the following "pseudo code".
We assign a row number to the absolute difference in seconds between the dates those with rowID of 1 win.
WTIH CTE AS (
SELECT A.*, B.* row_number() over (PARTITION BY A.e_mail
ORDER BY abs(datediff(second, cast(Tran_dateTime as Datetime), cast(InsterDate as DateTime)) desc) RN
FROM [crm].[SalesSampleTable] a
LEFT JOIN [crm].[SensitiveTable] b
on a.[E_MAIL] = b.[EMAIL])
SELECT * FROM CTE WHERE RN = 1

SQL query Splitting a column into Multiple rows divide by percentage

How to get percentage of a column and then inserting it as rows
Col1 item TotalAmount**
1 ABC 5558767.82
2 ABC 4747605.5
3 ABC 667377.69
4 ABC 3844204
6 CTB 100
7 CTB 500.52
I need to create a new column percentage for each item which is I have done as :-
Select item, (totalAmount/select sum(totalAmount) from table1) as Percentage
From table1
Group by item
Col1 item TotalAmount percentage
1 ABC 5558767.82 38
2 ABC 4747605.5 32
3 ABC 667377.69 5
4 ABC 3844204 26
6 CTB 100 17
7 CTB 500.52 83
Now, the complex part I have to calculate another amount by multiplying this percentage to an amount from another table say table2
ii) update the Total amount column by spilt the total amount column of table 1 into 2 rows – 1st row of the new Calculate PledgeAmount and 2nd row – (totalAmount – PledgeAmount)
*Select t1.percentage * t2.new amount as [PledgeAmount]
From table 1 join table2 where t1.item=t2.item*
. e.g. for col1 Amount of 5558767.82 will split into two rows.
Final Result sample for :-
Col1 item TotalAmount Type
1 ABC 363700.00 Pledge
1 ABC 5195067.82 Unpledge
....
I am using Temporary table to do calculations.
One of the way I think is to calculate the Pledged and Unpledged amount as new column and Pivot it but its huge table with hundreds of columns it will not perform fast.
Any other efficient way?
You can use a windowing function to solve this problem -- first in a sub-query calculate the total and then in the main query the percent:
Select *, (totalAmount/total_for_item)*100 as percent_of_total
from (
SELECT t.*,
SUM(totalAmount) OVER (PARTITION BY item) as total_for_item
FROM table t
) sub
First, let's get the total amount per item:
SELECT item, SUM( totalAmount ) as sumTotal
INTO #totalperitem
FROM table1
GROUP BY item
Now it's easy to get to the percentages:
SELECT t1.Col1,
t1.item,
t1.totalAmount,
t1.totalAmount/tpi.sumTotal*100 AS percentage
FROM table1 t1
INNER JOIN #totalperitem tpi on ...
Tricky part: Separate rows with/without match in table2. Can be done with a WHERE NOT EXISTS, or, my preference, with a single outer join:
SELECT t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END,
SUM( t1.totalAmount ) AS amount
FROM table1 t1
LEFT OUTER JOIN table2 tpledged ON t1. ... = tpledged. ...
GROUP BY t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END
The basic trick is to create an artificial column from the presence/absence of records in table2 and to also group by that artificial column.

SQL Query to generate an extra field from data in the table

I have a table with 3 fields like this sample table Tbl1
Person Cost FromDate
1 10 2009-1-1
1 20 2010-1-1
2 10 2009-1-1
I want to query it and get back the 3 fields and a generated field called ToDate that defaults to 2099-1-1 unless there is an actual ToDate implied from another entry for the person in the table.
select Person,Cost,FromDate,ToDate From Tbl1
Person Cost FromDate ToDate
1 10 2009-1-1 2010-1-1
1 20 2010-1-1 2099-1-1
2 10 2009-1-1 2099-1-1
You can select the minimum date from all dates that are after the record's date. If there is none you get NULL. With COALESCE you change NULL into the default date:
select
Person,
Cost,
FromDate,
coalesce((select min(FromDate) from Tbl1 later where later.FromDate > Tbl1.FromDate), '2099-01-01') as ToDate
From Tbl1
order by Person, FromDate;
Although Thorsten's answer is perfectly fine, it would be more efficient to use window-functions to match the derived end-dates.
;WITH nbrdTbl
AS ( SELECT Person, Cost, FromDate, row_nr = ROW_NUMBER() OVER (PARTITION BY Person ORDER BY FromDate ASC)
FROM Tbl1)
SELECT t.Person, t.Cost, t.FromDate, derived_end_date = COALESCE(nxt.FromDate, '9991231')
FROM nbrdTbl t
LEFT OUTER JOIN nbrdTbl nxt
ON nxt.Person = t.Person
AND nxt.row_nr = t.row_nr + 1
ORDER BY t.Person, t.FromDate
Doing a test on a 2000-records table it's about 3 times as efficient according to the Execution plan (78% vs 22%).

SQL Comparing values by date range in two tables

I have two tables in Sql Server 2008 -> SchedulePermanent and ScheduleImported
SchedulePermanent:
Id StartDate EndDate Value
1 01-01-2013 03-01-2013 100
2 03-01-2013 07-01-2013 200
3 07-01-2013 18-01-2013 300
CheduleImported:
Id StartDate EndDate Value
1 01-01-2013 04-01-2013 100
2 04-01-2013 06-01-2013 200
3 06-01-2013 15-01-2013 300
4 15-01-2013 18-01-2013 100
I want to insert into a result table only values that are not equal in the two schedules for each day.
Examlple: (OldValue: present in the permanent table, NewValue present in the import table)
Date OldValue NewValue
03-01-2013 200 100
06-01-2013 200 300
15-01-2013 300 100
16-01-2013 300 100
17-01-2013 300 100
18-01-2013 300 100
Do i have to split each table in temporary tables by date and then do the comparison for each date or is there a better way? (I saw some topics talking about Cross join but i have never used it)
Thank you,
I don't think you need a cross join, you should be able to do this with just an inner join.
select Date, SchedulePermanent.Value as OldValue, ScheduleImported.AsNewValue
from SchedulePermanent
join ScheduleImported on SchedulePermanent.StartDate = ScheduleImported.StartDate and SchedulePermanent.Value <> ScheduleImported.Value
However, that will only work to import values for dates that already exist in SchedulePermanent. To include values from imported that don't yet exist in permanent, it would look something like this:
select Date, SchedulePermanent.Value as OldValue, ScheduleImported.AsNewValue
from SchedulePermanent
right join ScheduleImported on SchedulePermanent.StartDate = ScheduleImported.StartDate
where SchedulePermanent.StartDate is null
If you want to overwrite the values, then you could check out a merge statement, instead.
MERGE SchedulePermanent AS target
USING (SELECT StartDate, Value from ScheduleImported) AS source
ON (target.StartDate = source.StartDate)
WHEN MATCHED THEN
UPDATE SET Value = source.Value
WHEN NOT MATCHED THEN
INSERT (StartDate, Value)
VALUES (source.StartDate, source.Value)
a simple way for you to get all the unequal records:
SELECT S.ID, S.StartDate, S.Value
FROM SchedulePermanent AS S
WHERE S.Value NOT EXSIST(
SELECT VALUE
FROM CheduleImported AS C
WHERE C.StartDate = S.StartDate AND C.EndDate = S.EndDate
)

mysql table inside join

I have table tblsale.
In this table i have field called BillType, which contains "s" and "r" (s = sale , r = returns )
The table has rougly 25 records. Of that, 7 records are "r", and rest of the records are "s".
How do I write the query so that my result set includes the following columns:
What is want exactly is below
Amount BillType Amount BillType Date
100 s 50 r 29-11-2010
120 s 20 r 28-11-2010
130 s 30 r 27-11-2010
140 s 50 r 26-11-2010
What you appear to want are the results of two queries, sales and returns, side by side. It can be done with a kludge like this:
select amount, sale, returnamount, returned, returndate
from
(
select amount, 1 as sale, 0 as returnamount, 0 as returned, '' as returndate
from sales where billtype='s'
union
select 0 as amount, 0 as sale, amount as returnamount, 1 as returned, date as returndate
from sales where billtype ='r'
)
You may have to cast date into a string representation. The unioned sets need the same column structure, so you create dummy columns. (You didn't ask for a sale date for sales.)
Or you can do this with CASE WHEN statement.
I'm not sure what you are asking, but maybe:
SELECT BillNo, VAT, BillType, AfterDiscount FROM tblsale WHERE BillType = 's';