I have an oracle query that gets data from two different tables, displaying them as:
Project | 20151 | 20152 | 20153 | 20154 | Project_1 | 20151_1 | 20152_1 | 20153_1 | 20154_1
-------------------------------------------------------------------------------------------
This does get the correct data for what I need, however I need a way of alternating the columns from each of the tables. E.g:
Project | Project_1 | 20151 | 20151_1 | ...
---------------------------------------
The query I have is as follows:
SELECT * FROM(
SELECT * FROM
(
SELECT planned.YEAR || planned.MONTH as AAA, planned.DAYS as Amount, PROJ.NAME AS Project
FROM PLANNED_RESOURCE_TAB planned, USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
)
PIVOT
(
SUM(Amount) FOR AAA in (20151,20152,20153,20154)
)
)
PLANNED__ RIGHT OUTER JOIN
(
SELECT * FROM
(
SELECT ACTUAL.YEAR || ACTUAL.MONTH as BBB, ACTUAL.DAYS as Amount, PROJ.NAME AS Project
FROM ACTUAL_RESOURCE_TAB actual, USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
)
PIVOT
(
SUM(Amount) FOR BBB in (20151,20152,20153,20154)
)
)ACTUAL__
ON PLANNED__.PROJECT = ACTUAL__.PROJECT
The data results will be displayed on a ASP.NET page, so if it is best to manipulate the columns on the page itself that is no problem. I mainly want to know whether it is possible to do it with SQL beforehand, and subsequently how I'd go about that.
The columns cannot be declared in the very first select statement, they will vary dependent on a users input. The values that are inside the pivot are just for my testing purposes, they will be generated dynamically from the ASP.NET page.
Thanks in advance!
Have you tried to replace the * with the retrieved columns of the most outer select? Something like this:
SELECT PLANNED__.Project,
ACTUAL__.Project,
PLANNED__."20151",
ACTUAL__."20151",
PLANNED__."20152",
ACTUAL__."20152",
PLANNED__."20153",
ACTUAL__."20153",
PLANNED__."20154",
ACTUAL__."20154"
FROM
(SELECT *
FROM
(SELECT planned.YEAR
|| planned.MONTH AS AAA,
planned.DAYS AS Amount,
PROJ.NAME AS Project
FROM PLANNED_RESOURCE_TAB planned,
USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
) PIVOT ( SUM(Amount) FOR AAA IN (20151,20152,20153,20154) )
) PLANNED__
RIGHT OUTER JOIN
(SELECT *
FROM
(SELECT ACTUAL.YEAR
|| ACTUAL.MONTH AS BBB,
ACTUAL.DAYS AS Amount,
PROJ.NAME AS Project
FROM ACTUAL_RESOURCE_TAB actual,
USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
) PIVOT ( SUM(Amount) FOR BBB IN (20151,20152,20153,20154) )
)ACTUAL__
ON PLANNED__.PROJECT = ACTUAL__.PROJECT
Related
I am in need of updating a few rows in a database at the same time. Now the issue is that I will only know the FIRST row's unique ID but I will have X number of other rows after that that will need to be updated with some data as well. I will know how many loops I will need in order to update them - just need to know how to go about doing it.
My query looks like this:
UPDATE bLine
SET #val1
WHERE theGuid = #val2;
Now the data for #val1 looks like this:
ID | qty_SerialNum | qty_Location | qty_Property | theGUID
---+---------------+--------------+--------------+---------------------------
5 | 6845fg56 | Home | NA | a45vz-u300-2bd-4710j-vf09
6 | fk469fkh | Dock#4 | NA |
7 | geww2 | Dock#1 | Local |
...
Which when in a string to send over for the #val1:
#val1 = qty_SerialNum = '6845fg56,fk469fkh,geww2',
qty_Location = 'Home,Dock#4,Dock#1',
qty_Property = 'NA,NA,Local'
#val2 = theGUID = 'a45vz-u300-2bd-4710j-vf09'
So that's all fine since I know the GUID of the first row that needs updating but every row after that I wont know.
So the 1st update would look like out of 3:
UPDATE
bLine
SET
(qty_SerialNum = '6845fg56',qty_Location = 'Home',qty_Property = 'NA' )
WHERE
theGuid = 'a45vz-u300-2bd-4710j-vf09';
And this would be the 2nd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'fk469fkh',qty_Location = 'Dock#4',qty_Property = 'NA' )
WHERE
???? = ????;
And finally this would be the 3rd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'geww2',qty_Location = 'Dock#1',qty_Property = 'Local' )
WHERE
???? = ????;
So the question here is - how can I loop through the next X rows and update that information with the 2,3,... values in #val1 (skipping the first since its already saved via the theGUID)?
You can treat this as a gaps and islands type problem where you need to update and island row(row containing guid as well all gap rows till next island).
I am taking some assumption here and below is a working demo
create table bline (ID int, qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100), theGUID varchar(100))
insert into bline values
(5,'random','garbage','existing','a45vz-u300-2bd-4710j-vf09')
,(6,'data','random','garbage', NULL)
,(7,'existing','data','.', NULL);
create table #V (qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100))
insert into #V values
('6845fg56','Home','NA'),
('fk469fkh','Dock#4','NA'),
('geww2','Dock#1','Local')
;with map as
(
select id,
rn=row_number() over ( order by id asc)
from bline
where theGUID is NOT NULL
),
mappedBline as
(
select
b1.*,
rn
from
bline b1 join
(
select b.id,
rn=max(rn)
from bline b
join map m
on b.id >=m.id
group by b.id
)b2
on b2.id=b1.id
),
updateSet as
(
select
M.*,
updaterow=row_number() over( order by M.id)
from mappedBline M join
mappedBline M2
on M.rn=M2.rn
and M2.theGUID = 'a45vz-u300-2bd-4710j-vf09' --#val2
)
update U
set
qty_SerialNum=V.qty_SerialNum,
qty_Location= V.qty_Location,
qty_Property =V.qty_Property
from
updateSet U join
( select
*, updaterow =row_number() over (order by (select NULL))
from #V
)V
on U.updaterow=V.updaterow
select * from bline
You have a very arcane problem. In general, SQL updates are not for "adjancent" rows in this fashion. However, you can make it work by storing the values in a (virtual) table and joining the two sides together:
with v as (
select v.*
from (values (1, '6845fg56', 'Home', 'NA'),
(2, 'fk469fkh', 'Dock#4', 'NA'),
(3, 'geww2', 'Dock#1', 'Local')
) v(seqnum, qty_SerialNum, qty_Location, qty_Property)
)
update b
set qty_SerialNum = v.qty_SerialNum,
qty_Location = v.qty_Location,
qty_Property = v.qty_Property
from (select top(3) b.*,
row_number() over (order by id) as seqnum
from bline b
where id >= (select id from bline where theGUID = 'a45vz-u300-2bd-4710j-vf09'
order by id
) b join
v
on b.seqnum = v.seqnum;
I want to get the Catalog of a selected supplier along with the last purchase price which is the column Costs in the CommandDetails table as shown bellow :
Product (idproduct, productName)
Command (idCommand, CommandCode, CommandDate, idSupplier)
CommandDetails(idCommandDetails, idProduct, Qty, idCommand, Costs)
Supplier (idSupplier, SupplierName, SupplierAddress)
SupplierCatalog (idSupplier, idProduct)
I tried the row_number() Over (Partition by ...) and other methods but i'm missing something, my brain said enough.
Desired Result :
--------------------------------------------------
| SupplierName| ProductName | CommandDate | Costs|
--------------------------------------------------
| SUP1 | P1 | 01/01/2018 | 3,06 |
| SUP1 | P6 | 01/01/2018 | 1,65 |
| SUP1 | P8 | 03/01/2018 | 5,20 |
| SUP1 | P9 | 05/01/2018 | 8,00 |
| SUP1 | P10 | 01/01/2018 | NULL |
--------------------------------------------------
Null in Costs for P10 when the product has never been ordered.
My last attempt :
SELECT
*
FROM
(SELECT
Sct.idsupplier,
SCt.idProduct,
SCD.PurchasePriceCmd Costs,
SCD.Qty,
P.ProCode,
P.ProName,
Row_number() OVER(PARTITION BY Sct.idProduct order by P.ProCode) rn
FROM SupplierCatalog SCt
LEFT JOIN CommandDetails SCD
ON SCD.idProduct = SCat.idProduct
LEFT JOIN Command a
ON a.idCommand = SCD.idCommand
LEFT OUTER JOIN StoreCommand b
ON a.idCommand = b.idCommand
INNER JOIN Product P
ON P.idProduct = SCt.idProduct) t
where Sct.idSupplier = 4 and rn = 1
You could also try this:
SELECT
s.supplierName,
p.productName,
latestCommandDetail.CommandDate,
latestCommandDetail.Costs
FROM Supplier s
INNER JOIN SupplierCatalog sc ON sc.idSupplier = s.idSupplier
INNER JOIN Product p ON p.idProduct = sc.idProduct
OUTER APPLY
(
SELECT TOP 1
c.CommandDate,
cd.Costs
FROM Command c
INNER JOIN CommandDetails cd ON cd.idCommand = c.idCommand
WHERE c.idSupplier = s.idSupplier AND cd.idProduct = p.idProduct
ORDER BY c.CommandDate DESC
) latestCommandDetail
WHERE s.idSupplier = 4
ORDER BY
s.supplierName,
p.productName
I don't have SQL Server going on this machine at the moment so you may need to tweak the syntax. The trick is just doing a link to a subquery that returns the top row sorted by the date descending.
I'm assuming the idSupplier for "SUP1" is 4 as per you example code above.
OUTER APPLY (the other optional join) will return nulls if no record is returned from the sub query. If you are only interested in products with prices use CROSS APPLY instead.
Also note that this query does not determine what to do if:
- there are two Commands for the same last date from the same supplier for the same product but with different Costs
- the same product is sold twice under the same Command but at different Costs
In both cases this can probably be handled by extending the sort order of the sub-query or by grouping/aggregating the sub-query.
Something like this ought to work:
;with cte as (
select a.idCommand, b.idProduct, row_number() over (partition by b.idProduct
order by a.CommandDate desc) as rn, a.CommandDate, b.Costs
from Command a
inner join CommandDetails b on a.idCommand = b.idCommand
)
select
c.SupplierName, e.productName, cte.CommandDate, cte.Costs
from Supplier c
left join SupplierCatalog d on c.idSupplier = d.idSupplier
left join Product e on d.idProduct = e.idproduct
left join cte on e.idProduct = cte.idproduct and cte.rn = 1
where c.idSupplier = #SupplierNumber;
You can probably replace the common table expression at the top with a subquery, or take some fields out of the CTE and join them in later.
I need to analyze data from SQL server table. Table contains data connected with qualifications of all employyes in the company and has the following structure (simplified):
| User | Qualification | DateOfQualificationAssignment |
| user000 | Junior | 2014-01-15 |
| user000 | Middle | 2014-02-15 |
| user001 | Middle | 2014-02-02 |
| user001 | Senior | 2014-03-18 |
| user002 | Senior | 2014-02-19 |
| user003 | Junior | 2014-03-04 |
I need the way to determine number of employees having given qualification for the concrete date. It should be some sort of analyze("Qualification", "Date") function returning the folowing for these types of input data:
analyze("Junior", '2014-01-20') - returns 1 (it is user user000)
analyze("Junior", '2014-02-20') - returns 0 (because user000 became Middle on 2014-02-15)
analyze("Middle", '2014-02-25') - returns 2 (because user000 and user001 are having Middle qualification on 2014-02-25)
analyze("Middle", '2014-03-28') - returns 1 (user000 is still Middle, but user001 became Senior on 2014-03-18)
Currently I have no idea how to handle this efficiently. What approach can be used to achieve my goal?
Think this should satisfy your requirements:
create function dbo.analyze(#qualification varchar(50), #date date)
returns int
as
begin
declare #result int;
with cte
as
(
select t.*, rank() over (partition by t.[User] order by t.DateOfQualificationAssignment desc) r
from theTable t -- no clue what the real table is named
where t.DateOfQualificationAssignment < #date
)
select #result = count(*)
from cte
where cte.r = 1 and cte.Qualification = #qualification
return #result;
end
go
Tested with your data:
create table theTable
(
[User] varchar(50) not null,
Qualification varchar(50) not null,
DateOfQualificationAssignment date not null
)
go
insert into theTable([User],Qualification,DateOfQualificationAssignment)
values
('user000','Junior','20140115'),
('user000','Middle','20140215'),
('user001','Middle','20140202'),
('user001','Senior','20140318'),
('user002','Senior','20140219'),
('user003','Junior','20140304')
go
and the results:
select dbo.analyze('Junior','20140120') --returns 1
go
select dbo.analyze('Junior','20140220') --returns 0
go
select dbo.analyze('Middle','20140225') --returns 2
go
select dbo.analyze('Middle','20140328') --returns 1
go
Use row_number() over() in a derived table to enumerate the rows on DateOfQualificationAssignment descending partitioned by User where DateOfQualificationAssignment is less than the date you want to check on.
In the main query you count the rows with the enumerated value 1 and Qualification.
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table T
(
[User] char(7),
Qualification char(6),
DateOfQualificationAssignment date
)
insert into T values
('user000', 'Junior', '2014-01-15'),
('user000', 'Middle', '2014-02-15'),
('user001', 'Middle', '2014-02-02'),
('user001', 'Senior', '2014-03-18'),
('user002', 'Senior', '2014-02-19'),
('user003', 'Junior', '2014-03-04')
Query 1:
declare #Qualification char(6) = 'Middle'
declare #Date date = '2014-03-28'
select count(*)
from (
select T.Qualification,
row_number() over(partition by T.[User] order by T.DateOfQualificationAssignment desc) as rn
from T
where T.DateOfQualificationAssignment < #Date
) as T
where T.rn = 1 and
T.Qualification = #Qualification
Results:
| COLUMN_0 |
|----------|
| 1 |
I have a table with "customer_id, date, installment_no, amount" columns. I want to get the information of last installment of each customer_id till today. here installment_no is int type and when a new installment is deposited, the installment_no is increased by 1 in new entry. My table look like:
CS1001 | 12-06-2013 | 1 | 2500
CS1002 | 19-06-2013 | 1 | 1600
CS1001 | 14-07-2013 | 2 | 2500
I want to get a sqlcommand statement for do so.
Group all records by customer_id, then order all customer's records by installment_no, and select only record with max installment_no from each group:
from c in customers
group c by c.customer_id into g
select g.OrderByDescending(x => x.installment_no).First()
Same with pure SQL if you don't use Linq
SELECT c.* FROM Customers c
INNER JOIN (
SELECT customer_id, MAX(installment_no) max_installment
FROM Customers
GROUP BY customer_id
) cmax
ON c.customer_id = cmax.customer_id
AND c.installment_no = cmax.max_installment
var result = list.GroupBy(x=>x.customer_id)
.Select(g=>g.OrderByDescending(y=>y.installment_no).First())
.ToList();
I want to show data like pivot grid. I am right now showing data like following. Please see following image or click on link.
http://screencast.com/t/CWeGy0vi
But I want above data like following:
http://screencast.com/t/ZTb2wk4cdmB
Any suggestion how to achieve this. Starting point. Should I use repeater?
Without seeing your table structure, etc. it is hard to give an exact answer. But I can suggest how you could perform this in SQL. You have some previous questions tagged with sql server so I am guessing that.
You can do this using both an UNPIVOT and a PIVOT:
;with unpiv as
(
select activity,
work,
Location+'_'+col as col,
value
from
(
select activity,
work,
cast(AssignedTasks as varchar(50)) AssignedTasks,
cast(CompletedTasks as varchar(50)) AchievedTasks,
Location
from yourtable
) src
unpivot
(
value
for col in (AssignedTasks, AchievedTasks)
) unpiv
),
piv as
(
select Activity,
work,
London_AssignedTasks,
London_AchievedTasks,
Geneva_AssignedTasks,
Geneva_AchievedTasks,
row_number() over(partition by activity order by activity, work) rn
from unpiv
pivot
(
max(value)
for col in (London_AssignedTasks, London_AchievedTasks,
Geneva_AssignedTasks, Geneva_AchievedTasks)
) piv
)
select case when rn = 1 then activity else '' end activity,
work,
London_AssignedTasks,
London_AchievedTasks,
Geneva_AssignedTasks,
Geneva_AchievedTasks
from piv
See SQL Fiddle with Demo.
The result is:
| ACTIVITY | WORK | LONDON_ASSIGNEDTASKS | LONDON_ACHIEVEDTASKS | GENEVA_ASSIGNEDTASKS | GENEVA_ACHIEVEDTASKS |
-------------------------------------------------------------------------------------------------------------------
| Activity 1 | Task 1 | 10 | 8 | 1 | 1 |
| | Task 2 | 15 | 15 | 100 | 25 |
| Activity 2 | Task 1 | 5 | 5 | 0 | 0 |
| | Task 2 | 0 | 0 | 2 | 2 |
| Activity 3 | Task 1 | 10 | 10 | 50 | 40 |
Edit #1, If you have an unknown or dynamic number of Locations then you can use dynamic SQL to return the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Location+'_'+t.tasks)
from yourtable
cross apply
(
select 'AssignedTasks' tasks
union all
select 'AchievedTasks'
) t
group by location, tasks
order by location
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = ';with unpiv as
(
select activity,
work,
Location+''_''+col as col,
value
from
(
select activity,
work,
cast(AssignedTasks as varchar(50)) AssignedTasks,
cast(CompletedTasks as varchar(50)) AchievedTasks,
Location
from yourtable
) src
unpivot
(
value
for col in (AssignedTasks, AchievedTasks)
) unpiv
),
piv as
(
select Activity,
work,
row_number() over(partition by activity order by activity, work) rn,
'+#cols+'
from unpiv
pivot
(
max(value)
for col in ('+#cols+')
) piv
)
select case when rn = 1 then activity else '''' end activity,
work,
'+#cols+'
from piv'
execute(#query)
See SQL Fiddle with Demo