I have two tables in MS Access database (Table1, Table2) with same columns namely Id, PartNo, Nomenclature, Quantity and Wksp. Id is the primary key in them. Id can be same in both tables. Now I want to write a query to get the output as shown under required output. How can I achieve this?
Table1
+----+--------+--------------+----------+----------+
| Id | PartNo | Nomenclature | Quantity | Wksp |
+----+--------+--------------+----------+----------+
| 1 | Part1 | Nomenc1 | 2 | Wksp1 |
| 2 | Part2 | Nomenc2 | 4 | Wksp1 |
| 3 | Part3 | Nomenc3 | 6 | Wksp1 |
| 4 | Part4 | Nomenc4 | 8 | Wksp1 |
+----+--------+--------------+----------+----------+
Table2
+----+--------+--------------+----------+-------+
| Id | PartNo | Nomenclature | Quantity | Wksp |
+----+--------+--------------+----------+-------+
| 1 | Part1 | Nomenc1 | 1 | Wksp2 |
| 2 | Part2 | Nomenc2 | 3 | Wksp2 |
| 3 | Part3 | Nomenc3 | 5 | Wksp2 |
| 4 | Part11 | Nomenc11 | 7 | Wksp2 |
| 5 | Part7 | Nomenc7 | 9 | Wksp2 |
+----+--------+--------------+----------+-------+
Required Output
+----+--------+--------------+-------+-------+
| Id | PartNo | Nomenclature | Wksp1 | Wksp2 |
+----+--------+--------------+-------+-------+
| 1 | Part1 | Nomenc1 | 2 | 1 |
| 2 | Part2 | Nomenc2 | 4 | 3 |
| 3 | Part3 | Nomenc3 | 6 | 5 |
| 4 | Part11 | Nomenc11 | 0 | 7 |
| 5 | Part7 | Nomenc7 | 0 | 9 |
| 6 | Part4 | Nomenc4 | 8 | 0 |
+----+--------+--------------+-------+-------+
First create a query to collect the IDs:
SELECT Id
FROM Table1
UNION
SELECT Id
FROM Tabel2;
Save this as, say, TBboth.
Then use this as source:
SELECT
TBboth.Id,
Nz(Table1!PartNo, Table2!PartNo) AS PartNo,
Nz(Table1!Nomenclature, Table2!Nomenclature) As Nomenclature,
Sum(Nz(Table1!Quantity,0)) AS Wksp1,
Sum(Nz(Table2!Quantity,0)) AS Wksp2
FROM
(TBboth
LEFT JOIN
TB1 ON TBboth.Id = TB1.Id)
LEFT JOIN
TB2 ON TBboth.Id = TB2.Id
GROUP BY
TBboth.Id,
Nz(Table1!PartNo, Table2!PartNo),
Nz(Table1!Nomenclature, Table2!Nomenclature);
Try this:
select Id , PartNo , Nomenclature, ISNULL(Wksp1,0) Wksp1, ISNULL(Wksp2,0) Wksp2
from
(
select Id , PartNo , Nomenclature ,Wksp, sum(Quantity) 'count' from Table1
group by Id , PartNo , Nomenclature,Wksp
union all
select Id , PartNo , Nomenclature ,Wksp, sum(Quantity) 'count' from Table2
group by Id , PartNo , Nomenclature,Wksp
) src
pivot
(
sum(count)
for Wksp in ([Wksp1], [Wksp2])
) P;
Try with This Method if you are using MS SQL Server
DECLARE #T1 TABLE
(
Id INT,
Part VARCHAR(20),
Qty INT,
Wksp VARCHAR(20) DEFAULT('Wksp 1')
)
DECLARE #T2 TABLE
(
Id INT,
Part VARCHAR(20),
Qty INT,
Wksp VARCHAR(20) DEFAULT('Wksp 2')
)
INSERT INTO #T1
VALUES(1,'Part1',10,'Wksp 1'),(2,'Part2',15,'Wksp 1'),(3,'Part3',12,'Wksp 1')
INSERT INTO #T1
VALUES(1,'Part1',10,'Wksp 2'),(2,'Part2',15,'Wksp 2'),(4,'Part4',20,'Wksp 2')
SELECT
Id = coalesce(a.id,b.id),
Part = coalesce(a.Part,b.Part),
Qty = coalesce(a.Qty,b.Qty),
Wksp =coalesce(a.Wksp,b.Wksp)
FROM #T1 A
FULL JOIN #T2 b
on a.Id = b.Id
Related
Here is my query which is in Oracle PL/SQL syntax, How can I Change it to SQL server format?
Any alternatives for Connect_by_isleaf?
(
select PARTY_KEY, ltrim(sys_connect_by_path(alt_name, '|'), '|') AS alt_name_list
from
(select PARTY_KEY, alt_name, row_number() over(partition by PARTY_KEY order by alt_name) rno
from (
select party_key, (select alt_name_type_desc from "CRMS"."PRJ_APP_ALT_NAME_TYPE" where alt_name_type_cd = alt_name_type) || ' - ' || alt_name as alt_name
from "CDD_PROFILES"."PRJ_PRF_ALT_NAME" order by party_key, alt_name_type
) alt
)
where connect_by_isleaf = 1
connect by PARTY_KEY = prior PARTY_KEY
and rno = prior rno+1
start with rno = 1
)
tried to use With AS clause but it is not working somehow.
Thanks in advance
The equivalent in SQL Server is called a "recursive CTE".
You can read about it here:
https://learn.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-2017
Oracle Hierarchical queries can be rewritten as recursive CTE statements in databases that support them (SQL Server included). A classic set of hierarchical data would be an organization hierarchy such as the one below:
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE ORGANIZATIONS
([ID] int primary key
, [ORG_NAME] varchar(30)
, [ORG_TYPE] varchar(30)
, [PARENT_ID] int foreign key references organizations)
;
INSERT INTO ORGANIZATIONS
([ID], [ORG_NAME], [ORG_TYPE], [PARENT_ID])
VALUES
(1, 'ACME Corp', 'Company', NULL),
(2, 'Finance', 'Division', 1),
(6, 'Accounts Payable', 'Department', 2),
(7, 'Accounts Receivables', 'Department', 2),
(8, 'Payroll', 'Department', 2),
(3, 'Operations', 'Division', 1),
(4, 'Human Resources', 'Division', 1),
(10, 'Benefits Admin', 'Department', 4),
(5, 'Marketing', 'Division', 1),
(9, 'Sales', 'Department', 5)
;
In the recursive t1 below the select statement before the union all is the anchor query and the select statement after the union all is the recursive part. The recursive part has exactly one reference to t1 in its from clause. The org_path column simulates oracles sys_connect_by_path function concatenating the org_names together. The level column simulates oracles LEVEL pseudo column and is utilized in the output query to determine the leaf status (is_leaf column) similar to oracles connect_by_isleaf pseudo column:
with t1(id, org_name, org_type, parent_id, org_path, level) as (
select o.*
, cast('|' + org_name as varchar(max))
, 1
from organizations o
where parent_id is null
union all
select o.*
, t1.org_path+cast('|'+o.org_name as varchar(max))
, t1.level+1
from organizations o
join t1
on t1.id = o.parent_id
)
select t1.*
, case when t1.level < lead(t1.level) over (order by org_path) then 0 else 1 end is_leaf
from t1 order by org_path
Results:
| id | org_name | org_type | parent_id | org_path | level | is_leaf |
|----|----------------------|------------|-----------|-------------------------------------------|-------|---------|
| 1 | ACME Corp | Company | (null) | |ACME Corp | 1 | 0 |
| 2 | Finance | Division | 1 | |ACME Corp|Finance | 2 | 0 |
| 6 | Accounts Payable | Department | 2 | |ACME Corp|Finance|Accounts Payable | 3 | 1 |
| 7 | Accounts Receivables | Department | 2 | |ACME Corp|Finance|Accounts Receivables | 3 | 1 |
| 8 | Payroll | Department | 2 | |ACME Corp|Finance|Payroll | 3 | 1 |
| 4 | Human Resources | Division | 1 | |ACME Corp|Human Resources | 2 | 0 |
| 10 | Benefits Admin | Department | 4 | |ACME Corp|Human Resources|Benefits Admin | 3 | 1 |
| 5 | Marketing | Division | 1 | |ACME Corp|Marketing | 2 | 0 |
| 9 | Sales | Department | 5 | |ACME Corp|Marketing|Sales | 3 | 1 |
| 3 | Operations | Division | 1 | |ACME Corp|Operations | 2 | 1 |
To select just the leaf nodes, change the output query from above to another CTE (T2) dropping the order by clause or moving it to final output query and limiting by the is_leaf column:
with t1(id, org_name, org_type, parent_id, org_path, level) as (
select o.*
, cast('|' + org_name as varchar(max))
, 1
from organizations o
where parent_id is null
union all
select o.*
, t1.org_path+cast('|'+o.org_name as varchar(max))
, t1.level+1
from organizations o
join t1
on t1.id = o.parent_id
), t2 as (
select t1.*
, case when t1.level < lead(t1.level) over (order by org_path) then 0 else 1 end is_leaf
from t1
)
select * from t2 where is_leaf = 1
Results:
| id | org_name | org_type | parent_id | org_path | level | is_leaf |
|----|----------------------|------------|-----------|-------------------------------------------|-------|---------|
| 6 | Accounts Payable | Department | 2 | |ACME Corp|Finance|Accounts Payable | 3 | 1 |
| 7 | Accounts Receivables | Department | 2 | |ACME Corp|Finance|Accounts Receivables | 3 | 1 |
| 8 | Payroll | Department | 2 | |ACME Corp|Finance|Payroll | 3 | 1 |
| 10 | Benefits Admin | Department | 4 | |ACME Corp|Human Resources|Benefits Admin | 3 | 1 |
| 9 | Sales | Department | 5 | |ACME Corp|Marketing|Sales | 3 | 1 |
| 3 | Operations | Division | 1 | |ACME Corp|Operations | 2 | 1 |
Alternatively if you realize that leaf nodes can be identified by their lack of child nodes, you can flip this on its head and start with the leaf nodes, and search up the tree, retaining all the original record values, building out the org_path in reverse, and passing along the next parent id as next_id. In the final output, stage, selecting only those records whose next_id is null will yield the same results as the prior query:
with t1(id, org_name, org_type, parent_id, org_path, level, next_id) as (
select o.*
, cast('|'+org_name as varchar(max))
, 1
, parent_id
from organizations o
where not exists (select 1 from organizations c where c.parent_id = o.id)
union all
select t1.id
, t1.org_name
, t1.org_type
, t1.parent_id
, cast('|'+p.org_name as varchar(max))+t1.org_path
, level+1
, p.parent_id
from organizations p
join t1
on t1.next_id = p.id
)
select * from t1 where next_id is null order by org_path
Results:
| id | org_name | org_type | parent_id | org_path | level | next_id |
|----|----------------------|------------|-----------|-------------------------------------------|-------|---------|
| 6 | Accounts Payable | Department | 2 | |ACME Corp|Finance|Accounts Payable | 3 | (null) |
| 7 | Accounts Receivables | Department | 2 | |ACME Corp|Finance|Accounts Receivables | 3 | (null) |
| 8 | Payroll | Department | 2 | |ACME Corp|Finance|Payroll | 3 | (null) |
| 10 | Benefits Admin | Department | 4 | |ACME Corp|Human Resources|Benefits Admin | 3 | (null) |
| 9 | Sales | Department | 5 | |ACME Corp|Marketing|Sales | 3 | (null) |
| 3 | Operations | Division | 1 | |ACME Corp|Operations | 2 | (null) |
One of these two methods may prove more performant than the other, but you'll need to try them each out on your data to see which one works better.
I am trying to query a table for data conversion but I am running into issues with the structure. I am guessing I need to Pivot then Unpivot but I am not sure where to begin.
This is my current table
+----+-----------+-------+---------+
| ID | field | value | date |
+----+-----------+-------+---------+
| 1 | Draw1 | 1500 | NULL |
| 1 | Draw1Date | NULL | 4/15/16 |
| 1 | Draw1Fee | 100 | NULL |
| 1 | Draw2 | 2000 | NULL |
| 1 | Draw2Date | NULL | 3/14/17 |
| 1 | Draw2Fee | 100 | NULL |
| 2 | Draw1 | 800 | NULL |
| 2 | Draw1Date | NULL | 4/16/18 |
| 2 | Draw1Fee | 150 | NULL |
| 2 | Draw2 | 760 | NULL |
| 2 | Draw2Date | NULL | 5/6/18 |
| 2 | Draw2Fee | 150 | NULL |
+----+-----------+-------+---------+
Result needed
+----+-------+---------+---------+------+
| ID | Draws | Amount | Date | Fee |
+----+-------+---------+---------+------+
| 1 | Draw1 | 1500 | 4/15/16 | 100 |
| 1 | Draw2 | 2000 | 3/14/17 | 100 |
| 2 | Draw1 | 800 | 4/16/18 | 150 |
| 2 | Draw2 | 760 | 5/6/18 | 150 |
+----+-------+---------+---------+------+
My answer works for provided data. if you are looking for more general solution, for more variety of data, you may try to find another answer. I've not used PIVOT/UNPIVOT.
Test Data:
create table #t (ID int, field varchar(20), [value] int, [date] date)
insert into #t values
(1 ,'Draw1' , 1500 , NULL ),
(1 ,'Draw1Date' , NULL , '4/15/16'),
(1 ,'Draw1Fee' , 100 , NULL ),
(1 ,'Draw2' , 2000 , NULL ),
(1 ,'Draw2Date' , NULL , '3/14/17'),
(1 ,'Draw2Fee' , 100 , NULL ),
(2 ,'Draw1' , 800 , NULL ),
(2 ,'Draw1Date' , NULL , '4/16/18'),
(2 ,'Draw1Fee' , 150 , NULL ),
(2 ,'Draw2' , 760 , NULL ),
(2 ,'Draw2Date' , NULL , '5/6/18' ),
(2 ,'Draw2Fee' , 150 , NULL )
Query:
;with ct as (
select ID, field
from #t
where field in ('Draw1', 'Draw2')
group by ID, field
)
select ct.ID, ct.field
, t1.[value] as Amount, t2.[date] as [Date], t3.[value] as Fee
from ct
inner join #t t1 on t1.ID = ct.ID and t1.field = ct.field
inner join #t t2 on t2.ID = ct.ID and t2.field = ct.field + 'Date'
inner join #t t3 on t3.ID = ct.ID and t3.field = ct.field + 'Fee'
Result:
ID field Amount Date Fee
1 Draw1 1500 2016-04-15 100
1 Draw2 2000 2017-03-14 100
2 Draw1 800 2018-04-16 150
2 Draw2 760 2018-05-06 150
Try this...
SELECT tblAmount.id AS ID,
tblAmount.field AS Draws,
Max(tblAmount.value) AS Amount,
Max(tblDate.[date]) AS [Date],
Max(tblFee.value) AS Fee
FROM tablename tblAmount
INNER JOIN (SELECT id, field, [date]
FROM tablename
WHERE [date] IS NOT NULL AND field LIKE '%Date') tblDate
ON tblAmount.id = tblDate.id
AND tblDate.field LIKE tblAmount.field + '%'
INNER JOIN (SELECT id, field, value
FROM tablename
WHERE value IS NOT NULL AND field LIKE '%Fee') tblFee
ON tblAmount.id = tblFee.id
WHERE tblAmount.value IS NOT NULL
AND tblAmount.field NOT LIKE '%Fee'
AND tblAmount.field NOT LIKE '%Date'
GROUP BY tblAmount.id, tblAmount.field
ORDER BY tblAmount.id, tblAmount.field
Output
+----+-------+--------+------------+-----+
| ID | Draws | Amount | Date | Fee |
+----+-------+--------+------------+-----+
| 1 | Draw1 | 1500 | 2016-04-15 | 100 |
| 1 | Draw2 | 2000 | 2017-03-14 | 100 |
| 2 | Draw1 | 800 | 2018-04-16 | 150 |
| 2 | Draw2 | 760 | 2018-05-06 | 150 |
+----+-------+--------+------------+-----+
Demo: http://www.sqlfiddle.com/#!18/97688/101/0
I would simply do:
select id, left(field, 5),
max(case when len(field) = 5 then value end) as value,
max(case when field like '%date' then value end) as date,
sum(case when field like '%fee' then value end) as fee
from t
group by id, left(field, 5);
If your field is really more complex, do you are looking for date and fee at the end, by anything before, then use cross apply:
select t.id, v.draws,
max(case when t.field = v.draws then value end) as value,
max(case when t.field like '%date' then value end) as date,
sum(case when t.field like '%fee' then value end) as fee
from t cross apply
(values (replace(replace(field, 'date', ''), 'fee', '')) v(draws)
group by id, v.draws;
I have this table of articles
---------------------------------------------
| Article | Location | existence |
---------------------------------------------
| 200116 | cc3111 | 1 |
---------------------------------------------
| 200116 | ee3091 | 1 |
---------------------------------------------
And this count table
----------------------------------------------------
| Article | Location | Quantity |
----------------------------------------------------
| 200116 | cc3111 | 10 |
----------------------------------------------------
| 200116 | EE3091 | 8 |
----------------------------------------------------
| 200116 | EE2102 | 5 |
----------------------------------------------------
| 200116 | DD5131 | 7 |
----------------------------------------------------
What I'm trying to do is give me one result like the next.
--------------------------------------------------------------------------
| Article | Location | Existence | Quantity | DIF |
--------------------------------------------------------------------------
| 200116 | CC3111 | 1 | 10 | 9 |
--------------------------------------------------------------------------
| 200116 | EE3091 | 1 | 8 | 7 |
--------------------------------------------------------------------------
| 200116 | DD5131 | 0 | 7 | 7 |
--------------------------------------------------------------------------
| 200116 | EE2102 | 0 | 5 | 5 |
----------------------------- -------------------------------------------
But without doing it with a Union since then I have to make another query, I just do not know how to join it without usingUnion.
-SOME HELP TO PERFORM THIS QUERY
-I do not know how to add the location in the same column
This looks like a left join:
select t2.*, coalesce(t1.existence, 0) as existence,
(t2.quantity - coalesce(t1.existence, 0)) as dif
from t2 left join
t1
on t2.article = t1.article and t2.location = t1.location;
Here's another option...
IF OBJECT_ID('tempdb..#Article', 'U') IS NOT NULL
DROP TABLE #Article;
CREATE TABLE #Article (
Article INT NOT NULL,
Location CHAR(6) NOT NULL,
Existance INT NOT NULL
);
IF OBJECT_ID('tempdb..#Counts', 'U') IS NOT NULL
DROP TABLE #Counts;
CREATE TABLE #Counts (
Article INT NOT NULL,
Location CHAR(6) NOT NULL,
Quantity INT NOT NULL
);
INSERT #Article(Article, Location, Existance) VALUES
(200116, 'cc3111', 1), (200116, 'ee3091', 1);
INSERT #Counts(Article, Location, Quantity) VALUES
(200116, 'cc3111', 10), (200116, 'EE3091', 8),
(200116, 'EE2102', 5), (200116, 'DD5131', 10);
SELECT * FROM #Article a;
SELECT * FROM #Counts c;
--=====================================================
SELECT
Article = COALESCE(a.Article, c.Article),
Location = COALESCE(a.Location, c.Location),
Existance = COALESCE(a.Existance, 0),
Quantity = COALESCE(c.Quantity, 0),
DIF = COALESCE(c.Quantity, 0) - COALESCE(a.Existance, 0)
FROM
#Article a
FULL JOIN #Counts c
ON a.Article = c.Article
AND a.Location = c.Location;
You need to JOIN these tables instead of doing a UNION. The former will put columns side by side, whereas the latter will stack two similar sources together...
Got something like:
+-------+------+-------+
| count | id | grade |
+-------+------+-------+
| 1 | 0 | A |
| 2 | 0 | B |
| 1 | 1 | F |
| 3 | 1 | D |
| 5 | 2 | B |
| 1 | 2 | C |
I need:
+-----+---+----+---+---+---+
| id | A | B | C | D | F |
+-----+---+----+---+---+---+
| 0 | 1 | 2 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 1 |
| 2 | 0 | 5 | 1 | 0 | 0 |
I don't know if I can even do this. I can group by id but how would you read the count value for each grade column?
CREATE TABLE #MyTable(_count INT,id INT , grade VARCHAR(10))
INSERT INTO #MyTable( _count ,id , grade )
SELECT 1,0,'A' UNION ALL
SELECT 2,0,'B' UNION ALL
SELECT 1,1,'F' UNION ALL
SELECT 3,1,'D' UNION ALL
SELECT 5,2,'B' UNION ALL
SELECT 1,2,'C'
SELECT *
FROM
(
SELECT _count ,id ,grade
FROM #MyTable
)A
PIVOT
(
MAX(_count) FOR grade IN ([A],[B],[C],[D],[F])
)P
You need a "pivot" table or "cross-tabulation". You can use a combination of aggregation and CASE statements, or, more elegantly the crosstab() function provided by the additional module tablefunc. All basics here:
PostgreSQL Crosstab Query
Since not all keys in grade have values, you need the 2-parameter form. Like this:
SELECT * FROM crosstab(
'SELECT id, grade, count FROM table ORDER BY 1,2'
, $$SELECT unnest('{A,B,C,D,F}'::text[])$$
) ct(id text, "A" int, "B" int, "C" int, "D" int, "F" int);
See the SQL table below:
+------------+---------+
| Category | RevCode |
+------------+---------+
| 100.10.10 | 2 |
| 100.10.10 | 3 |
| 100.50.10 | 2 |
| 100.50.15 | 2 |
| 100.50.15 | 3 |
| 1000.80.10 | 3 |
| 200.10.10 | 3 |
| 200.50.10 | 3 |
| 200.80.10 | 3 |
| 2000.20.10 | 2 |
| 2000.20.10 | 3 |
| 2000.20.20 | 2 |
| 2000.20.20 | 3 |
| 2000.20.30 | 2 |
+------------+---------+
How can I delete all line items with the Rev Code of 3 where the following condition is met:
A Category has a Rev Code of both '2' and '3'.
For example:
+-----------+---------+
| Category | RevCode |
+-----------+---------+
| 100.10.10 | 2 |
| 100.10.10 | 3 |
+-----------+---------+
The above table will become:
+-----------+---------+
| Category | RevCode |
+-----------+---------+
| 100.10.10 | 2 |
+-----------+---------+
You can use sub_query with having clause like this:
delete from del_table
where RevCode = '3'
and Category in
(select Category from del_table
where RevCode in ('2','3')
group by Category
having count(distinct RevCode) =2 )
this statement may not be efficient, you can use exists clause instead of in clause.
Thanks for Charlesliam's comment. I use sql fiddle tested two cases below.
case1 :
create table del_table(Category varchar(20),RevCode Int);
INSERT INTO del_table VALUES
('100.10.10',2 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',3 )
result after deletion:
CATEGORY REVCODE
100.10.10 2
100.50.10 2
100.50.15 3
case2(a Category have more than two rows with duplicate RevCode) :
create table del_table(Category varchar(20),RevCode Int);
INSERT INTO del_table VALUES
('100.10.10',2 ),
('100.10.10',2 ),
('100.10.10',3 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',3 )
result after deletion:
CATEGORY REVCODE
100.10.10 2
100.10.10 2
100.50.10 2
100.50.15 3
See whether this helps you.
DECLARE #A TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, CATEGORY VARCHAR(20),REVCODE INT)
INSERT INTO #A VALUES
('100.10.10',2 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',2 ),
('100.50.15',3 ),
('1000.80.10',3),
('200.10.10',3 ),
('200.50.10',3 ),
('200.80.10',3 ),
('2000.20.10',2),
('2000.20.10',3),
('2000.20.20',2),
('2000.20.20',3),
('2000.20.30',2)
SELECT * FROM #A
Table:
Query:
DELETE LU
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CATEGORY ORDER BY REVCODE) ROW
FROM #A A
WHERE A.REVCODE IN (2,3)
) LU
WHERE LU.ROW = 2
SELECT * FROM #A
Result: