SQL displaying results based on a value in column - sql

So I have 2 tables in web SQL , one of them looks like this(there are thousands of rows):
customer_number | order_number
--------------------------------------------
1234 12
1234 13
1234 14
6793 20
6793 22
3210 53
etc.
And the other table like this(also thousands of rows):
customer_number | first_purchase_year
----------------------------------------------------
1234 2010
5313 2001
1632 2018
9853 2017
6793 2000
3210 2005
etc.
I have this code to select 10 customers from the first table and list all their purchases:
select top 10 * from
(select distinct t1.customer_number,
stuff((select '' + t2.order_number
from orders t2
where t1.customer_number = t2.customer_number
for xml path(''), type
).value('.','NVARCHAR(MAX)')
,1,0,'')DATA
from orders t1) a
Whch outputs this:
customer_number | order_number
--------------------------------------------
1234 12 13 14
6793 20 22
3210 53
What I need to do is ONLY display 10 random customers that have first_purchase_year > 2010.
I am not sure how to check if first_purchase_year corresponding to a customer_number is greater than 2010.
Thank you!

You just need to fix the subquery in the outer from clause:
select c.customer_number,
stuff((select '' + o2.order_number
from orders o2
where c.customer_number = o2.customer_number
for xml path(''), type
).value('.','NVARCHAR(MAX)'
), 1, 0, ''
) as data
from (select top (10) c.customer_number
from table2 c
where c.first_purchase_year > 2010
) c;

Related

Linear Interpolation in SQL

I work with crashes and mileage for the same year which is Year in table. Crashes are are there for every record, but annual mileage is not. NULLs for mileage could be at the beginning or at the end of the time period for certain customer. Also, couple of annual mileage records can be missing as well. I do not know how to overcome this. I try to do it in CASE statement but then I do not know how to code it properly. Issue needs to be resolved in SQL and use SQL Server.
This is how the output looks like and I need to have mileage for every single year for each customer.
The info I am pulling from is proprietary database and the records themselves should be untouched as is. I just need code in query which will modify my current output to output where I have mileage for every year. I appreciate any input!
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
NULL
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
NULL
2015
123
15
6346747
2016
123
0
NULL
2017
123
2
534534
2018
123
7
NULL
2019
123
11
NULL
2020
123
15
565435
2021
123
12
474567546
2022
123
7
NULL
Desired Results
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
175399 (prior value is taken)
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
34900100 (avg of 2 adjacent values)
2015
123
15
6346747
2016
123
0
3440641 (avg of 2 adjacent values)
2017
123
2
534534
2018
123
7
534534 ( prior value is taken)
2019
123
11
549985 (avg of 2 adjacent values)
2020
123
15
565435
2021
123
12
474567546
2022
123
7
474567546 (prior value is taken)
SELECT Year,
Customer,
Crashes,
CASE
WHEN Annual_Mlg IS NOT NULL THEN Annual_Mlg
WHEN Annual_Mlg IS NULL THEN
CASE
WHEN PREV.Annual_Mlg IS NOT NULL
AND NEXT.Annual_Mlg IS NOT NULL
THEN ( PREV.Annual_Mlg + NEXT.Annual_Mlg ) / 2
ELSE 0
END
END AS Annual_Mlg
FROM #table
The above code doesn't work, but I just need to start somehow and that what I have currently.
I understand what I need to do I just do not know how to code it in SQL.
After i applied row_number () function i got this output for first 2 clients and for the rest of the 4 clients row_number() function gave correct output. i have no idea why is that. I thought may be because i used "full join" before to combine mileage and crashes table?
enter image description here
Your use of #table tells me that you're using MS SQL Server (a temporary table, probably in a stored procedure).
You want to:
select all the rows in #table
joined with the matching row (if any) for the previous year, and
joined with the matching row (if any) for the next year
Then it's easy. Assuming the primary key on your #table is composed of the year and customer columns, something like this ought to do you:
select t.year ,
t.customer ,
t.crashes ,
annual_milage = coalesce(
t.annual_milage ,
( coalesce( p.annual_mileage, 0 ) +
coalesce( n.annual_mileage, 0 )
) / 2
)
from #table t -- take all the rows
left join #table p on p.year = t.year - 1 -- with the matching row for
and p.customer = t.customer -- the previous year (if any)
left join #table n on n.year = t.year + 1 -- and the matching row for
and n.customer = t.customer -- the next year (if any)
Notes:
What value you default to if the previous or next year doesn't exist is up to you (zero? some arbitrary value?)
Is the previous/next year guaranteed to be the current year +/- 1?
If not, you may have to use derived tables as the source for the
prev/next data, selecting the closest previous/next year (that sort
of thing rather complicates the query significantly).
Edited To Note:
If you have discontiguous years for each customer such that the "previous" and "next" years for a given customer are not necessarily the current year +/- 1, then something like this is probably the most straightforward way to find the previous/next year.
We use a derived table in our from clause, and assign a sequential number in lieu of year for each customer, using the ranking function row_number() function. This query, then
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
would produce results along these lines:
row_nbr
customer
year
...
1
123
1992
...
2
123
1993
...
3
123
1995
...
4
123
2020
...
1
456
2001
...
2
456
2005
...
3
456
2020
...
And that leads us to this:
select year = t.year ,
customer = t.customer ,
crashes = t.crashes ,
annual_mileage = coalesce(
t.mileage,
coalesce(
t.annual_mileage,
(
coalesce(p.annual_mileage,0) +
coalesce(n.annual_mileage,0)
) / 2
),
)
from (
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
) t
left join #table p on p.customer = t.customer and p.row_nbr = t.row_nbr-1
left join #table n on n.customer = t.customer and n.row_nbr = t.row_nbr+1

SQL Crosstab with undetermined columns

I see a lot of similar questions, but almost all of them wind up grouping results as column names (Column names based on results), mine is a more simple list. I don't care if it uses dynamic SQL or not (I'd think it has to).
Please don't tell me I need to restructure the tables, I'm working from a legacy system and don't have that option.
Basically, I just need a list of all valid table "B" entries that match a given record from table "A", in a row.
I don't have any code sample yet, because I'm not seeing a way to set this up correctly.
Table: Customer c
CustID Name
1 Bill Smith
2 Jim Jones
3 Mary Adams
4 Wendy Williams
Table: Debt d
CustID Creditor Balance
1 ABC Loans 245
1 Citibank 815
2 Soprano Financial 74000
3 Citibank 24
3 Soprano Financial 93000
3 Wells Fargo 275
3 Midwestern S&L 2500
4 ABC Loans 1500
4 Fred's Payday Loan 1000
Desired Output:
Name Cred1 Bal1 Cred2 Bal2 Cred3 Bal3 Cred4 Bal4
Bill Smith ABC Loans 245 Citibank 815 (NULL) (NULL) (NULL) (NULL)
Jim Jones Soprano Financial 74000 (NULL) (NULL) (NULL) (NULL) (NULL) (NULL)
Mary Adams Citibank 24 Soprano Finanacial 93000 Wells Fargo 275 Midwestern S&L 2500
Wendy Williams ABC Loans 1500 Fred's Payday Loan 1000 (NULL) (NULL) (NULL) (NULL)
Basically, I probably have to collect some kind of count of the most number of records for any specific "CustomerID", and define the output columns based on that. If this has already been answered, feel free to link and close this out, I did not see this specific scenario when I did my search.
Here is another dynamic approach. We use Row_Number() to create the minimal number of columns.
Example
Declare #SQL varchar(max) = Stuff((Select Distinct ','+QuoteName(concat('Cred',ColNr))
+','+QuoteName(concat('Bal',ColNr))
From (Select ColNr=Row_Number() over (Partition By CustID Order By Creditor) From Debt ) A
Order By 1
For XML Path('')),1,1,'')
Select #SQL = '
Select *
From (
Select C.Name
,B.*
From (
Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
From Debt
) A
Cross Apply (values (concat(''Cred'',ColNr),[Creditor])
,(concat(''Bal'' ,ColNr) ,cast(Balance as varchar(25)))
) B (Item,Value)
Join Customer C on A.CustID=C.CustID
) A
Pivot (max([Value]) For [Item] in (' + #SQL + ') ) p'
--Print #SQL
Exec(#SQL);
Returns
If if Helps, the Generated SQL Looks Like This:
Select *
From (
Select C.Name
,B.*
From (
Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
From Debt
) A
Cross Apply (values (concat('Cred',ColNr),[Creditor])
,(concat('Bal' ,ColNr) ,cast(Balance as varchar(25)))
) B (Item,Value)
Join Customer C on A.CustID=C.CustID
) A
Pivot (max([Value]) For [Item] in ([Cred1],[Bal1],[Cred2],[Bal2],[Cred3],[Bal3],[Cred4],[Bal4]) ) p
Just for the Visualization, the query "feeding" the Pivot generates:
I will guess you are already know how to use cross tab so you only need to prepare your data to use it.
STEP 1: Join both tables:
SELECT c.Name, d.Creditor, d.Balance
FROM Customer c
JOIN Debt d
ON c.CustID = d.CustID
STEP 2: Include a row number to each element related to the customer you are going to use to cross tab
SELECT c.Name, d.Creditor, d.Balance,
ROW_NUMBER() over (PARTITION BY Name ORDER BY creditor) as rndebt_tab
FROM Customer c
JOIN Debt d
ON c.CustID = d.CustID
Now you have:
CustID Creditor Balance rn
1 ABC Loans 245 1
1 Citibank 815 2
2 Soprano Financial 74000 1
3 Citibank 24 1
3 Soprano Financial 93000 2
3 Wells Fargo 275 3
3 Midwestern S&L 2500 4
4 ABC Loans 1500 1
4 Fred's Payday Loan 1000 2
STEP 3: Create the SOURCE for the cross tab
WITH cte as (
<query from step2>
)
SELECT Name,
'CREDITOR_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
Creditor as Value
FROM cte
UNION all
SELECT Name,
'DEBT_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
CAST(Balance as VARCHAR(max)) as Value
FROM cte
Now you have:
CustID cross_tab Value
1 CREDITOR_001 ABC Loans
1 CREDITOR_002 Citibank
2 CREDITOR_001 Soprano Financial
3 CREDITOR_001 Citibank
3 CREDITOR_002 Soprano Financial
3 CREDITOR_003 Wells Fargo
3 CREDITOR_004 Midwestern S&L
4 CREDITOR_001 ABC Loans
4 CREDITOR_002 Fred's Payday Loan
1 DEBT_001 245
1 DEBT_002 815
2 DEBT_001 ` 74000
3 DEBT_001 24
3 DEBT_002 93000
3 DEBT_003 275
3 DEBT_004 2500
4 DEBT_001 1500
4 DEBT_002 1000
EDIT: I use CustID instead of Name on the example but too lazy to change now.

Oracle 10g unpivot returning values in one column and creating month column

We just found out that the new database we have been given access to is Oracle 10g, so we are unable to use fcn like UNPIVOT.
We have a table like this..
SUBMISSION COUNTRY CPM_ID PFM_ID T_AREA CNTRY_CODE V_TYPE RES_CAT JAN_2014 FEB_2014
01-JUN-2014 USA 10 24 TEST1 USA V1 210 5 10
01-AUG-2014 UK 20 30 TEST2 UK V1 213 20 30
The desired output would look like this...
SUBMISSION COUNTRY CPM_ID PFM_ID T_AREA CNTRY_CODE V_TYPE RES_CAT MONTH VALUE
01-JUN-2014 USA 10 24 TEST1 USA V1 210 01-JAN-2014 5
01-JUN-2014 USA 10 24 TEST1 USA V1 210 01-FEB-2014 10
01-AUG-2014 UK 20 30 TEST2 UK V1 213 01-JAN-2014 20
01-AUG-2014 UK 20 30 TEST2 UK V1 213 01-FEB-2014 30
I am working with a query like this...but I cannot get the month column to come out right...
select *
from (select t.submission,
t.country,
t.cpm_id,
t.pfm_id,
t.t_area,
t.cntry_code,
t.v_type,
t.res_cat,
(case
when n.n = 1 then JAN-2014
when n.n = 1 then FEB-2014 end) as value
from table1 t cross join
(select FEB_2014 as n from dual union all
select FEB_2014 from dual) n
) s
where value is not null;
Thanks for your help,
I would do:
select t.submission,
t.country,
t.cpm_id,
t.pfm_id,
t.t_area,
t.cntry_code,
t.v_type,
t.res_cat,
n.d,
case when n.d = '01-JAN-2014' then t.jan_2014 else t.feb_2014 end value
from table1 t
cross join
(
select '01-JAN-2014' d from dual
union all
select '01-FEB-2014' d from dual
) n;

Calculate MonthlyVolume - PIVOT SQL

I am using SQL server 2008 and I have following Table with millions of rows...Here are few sample records
Serial_Num ReadingDate M_Counter Dyn_Counter
XYZ 3/15/2014 100 190
XYZ 4/18/2014 140 240
XYZ 5/18/2014 200 380
ABC 3/12/2014 45 40
ABC 4/19/2014 120 110
ABC 5/21/2014 130 155
This table will always have only one reading for each month and no missing months....
and I would like calculate M_Counter and Dyn_Counter values for each month, For an example XYZ -> May month calculated counter value should be 60 = 200 (05/18/2014 value) - 140 (04/18/2014 value). I would like to insert data into another table in following way.
CalculatedYear CalculatedMonth Serial_Num M_Counter_Calc Dyn_Counter_Calc
2014 4 XYZ 40 50
2014 5 XYZ 60 140
2014 4 ABC 75 70
2014 5 ABC 10 45
Any help really appreciated!
If you're using MS SQL, something like this should work. The concept is to sort the dataset based on Serial_Num and ReadingDate. Add a sequential Row ID and store into a temp table. Join the table onto itself such that you match up the current row with the previous row where the serial numbers still match. If there wasn't a prior month's reading, the value will be null. We use Isnull( x, 0) to account for this when doing the calculations.
declare #Temp1 table
(
RowID int,
Serial_Num varchar(3),
ReadingDate datetime,
M_Counter int,
Dyn_Counter int
)
insert into #Temp1
select ROW_NUMBER() over (order by Serial_Num, ReadingDate), *
from MyTable T
select
Year(T1.ReadingDate) As CalculatedYear,
Month(T1.ReadingDate) as CalculatedMonth,
T1.Serial_Num,
T1.M_Counter - ISNULL(T2.M_Counter,0) as Calculated_M_Counter,
T1.Dyn_Counter - isnull(T2.Dyn_Counter,0) as Calculated_Dyn_Counter
from #Temp1 T1
left outer join #Temp1 T2 on T1.RowID = T2.RowID + 1 and T1.Serial_Num = T2.Serial_Num
order by T1.Serial_Num, Year(T1.ReadingDate), Month(T1.ReadingDate)

Split a Table into 2 or more Tables based on Column value

I have a Table called "MIVTable" which has the following records,
MIVID Quantity Value
------ ---------- --------
14 10 3000
14 20 3500
14 15 2000
15 20 3000
15 50 7500
16 25 2000
Here, I need to store the above Table into two tables such as "HeaderTbl" and "DetailTbl" based on the MIVID as follows:
HeaderTbl:
HID MIVID TotalQuantity TotalValue
----- ------- ------------- -----------
1 14 45 8500
2 15 70 10500
3 16 25 2000
Here HID is the Primary Key with Identity Column.
DetailTbl:
HID MIVID Quantity Value
----- ------- ------------ -------
1 14 10 3000
1 14 20 3500
1 14 15 2000
2 15 20 3000
2 15 50 7500
3 16 25 2000
Suppose, if the MIVTable contains 4 different MIVID means, then 4 row should be created based on the MIVID on the HeaderTbl. How to do this?
To insert records in HeaderTbl from MIVTable use this: (HID should be auto increment)
INSERT INTO HeaderTbl
([MIVID], [TotalQuantity], [TotalValue])
SELECT MIVID, SUM(Quantity), SUM(Value) FROM MIVTable GROUP BY MIVID;
To insert records in DetailTbl from HeaderTbl and MIVTable use this:
INSERT INTO DetailTbl
([HID], [MIVID], [Quantity], [Value])
SELECT H.HID, M.*
FROM HeaderTbl H
INNER JOIN MIVTable M
ON H.MIVID = M.MIVID;
Look at this SQLFiddle
Here you need to use INSERT INTO SELECT statement to insert data from one table to another. You can also use JOIN in such statement as I did it for DetailTbl.
You would generate the HeaderTbl using RANK() SQL Server function, as follows:
SELECT RANK() OVER (ORDER BY MIVID) as HID, MIVID, TotalQuantity, TotalValue
FROM
(
SELECT
MIVID,
SUM(Quantity) as TotalQuantity,
SUM(Value) as TotalValue
FROM MIVTable GROUP BY MIVID
) AS A
and the Detail table using the ROW_NUMBER() SQL Server function, as follows:
SELECT
ROW_NUMBER() OVER (ORDER BY MIVID) AS HID,
MIVID,
Quantity,
Value
FROM MIVTable