SQL Server to display a view to show products at parent and child level - sql

I have a table like this:
+----+----------+----------+-----------+
| ID | Name | Is_Group | Parent_id |
+----+----------+----------+-----------+
| 1 | Clothes | 1 | Null |
| 2 | Food | 1 | Null |
| 3 | fastfood | 1 | 2 |
| 4 | T-shirt | 0 | 1 |
| 5 | skirt | 0 | 1 |
| 6 | pizza | 0 | 3 |
| 7 | snack | 0 | 3 |
+----+----------+----------+-----------+
I would like to have a horizontal representation to use for reporting such as:
+----+---------+---------+----------+
| ID | Name | level1 | level2 |
+----+---------+---------+----------+
| 4 | T-shirt | Clothes | Null |
| 5 | skirt | Clothes | Null |
| 6 | pizza | Food | fastfood |
| 7 | snack | Food | fastfood |
+----+---------+---------+----------+
Would anyone know how to do this?

You can use two levels of left join:
select t.*,
coalesce(tpp.name, tp.name) as level1,
(case when tpp.name is not null then tp.name end) as level2
from t left join
t tp
on t.parent_id = tp.id left join
t tpp
on tp.parent_id = tpp.parent_id
where not exists (select 1
from t tc
where tc.parent_id = t.id);

Related

SQL Merge two Tables

Let's say I have these 2 tables:
ArticleTBL
+---------+----------+-------------+------------+
|articleid| typeid | price | user |
+---------+----------+-------------+------------+
| 0 | 2 | 1 | 122 |
| 1 | 3 | 2 | 344 |
| 2 | 3 | 1 | 455 |
| 3 | 1 | 4 | 34 |
+---------+----------+-------------+------------+
TypeTBL
+---------+----------+-------------+
|typeid | type | factory |
+---------+----------+-------------+
| 0 | wooden | factry1 |
| 1 | plastic | factry2 |
| 2 | metal | factry3 |
| 3 | sth. | factry4 |
+---------+----------+-------------+
How do I request all this information only with articleid for each row?
Isn't this what you want? Read more
SELECT a.articleid,
a.price.a.USER,
t.typeid,
t.type,
t.factory
FROM form ArticleTBL a
INNER JOIN typetbl t
ON a.typeid = t.typeid
WHERE a.articleid = 0

SQL Query - Add column data from another table adding nulls

I have 2 tables, tableStock and tableParts:
tableStock
+----+----------+-------------+
| ID | Num_Part | Description |
+----+----------+-------------+
| 1 | sr37 | plate |
+----+----------+-------------+
| 2 | sr56 | punch |
+----+----------+-------------+
| 3 | sl30 | crimper |
+----+----------+-------------+
| 4 | mp11 | holder |
+----+----------+-------------+
tableParts
+----+----------+-------+
| ID | Location | Stock |
+----+----------+-------+
| 1 | A | 2 |
+----+----------+-------+
| 3 | B | 5 |
+----+----------+-------+
| 5 | C | 2 |
+----+----------+-------+
| 7 | A | 1 |
+----+----------+-------+
And I just want to do this:
+----+----------+-------------+----------+-------+
| ID | Num_Part | Description | Location | Stock |
+----+----------+-------------+----------+-------+
| 1 | sr37 | plate | A | 2 |
+----+----------+-------------+----------+-------+
| 2 | sr56 | punch | NULL | NULL |
+----+----------+-------------+----------+-------+
| 3 | sl30 | crimper | B | 5 |
+----+----------+-------------+----------+-------+
| 4 | mp11 | holder | NULL | NULL |
+----+----------+-------------+----------+-------+
List ALL the rows of the first table and if the second table has the info, in this case 'location' and 'stock', add to the column, if not, just null.
I have been using inner and left join but some rows of the first table disappear because the lack of data in the second one:
select tableStock.ID, tableStock.Num_Part, tableStock.Description, tableParts.Location, tableParts.Stock from tableStock inner join tableParts on tableStock.ID = tableParts.ID;
What can I do?
You can use left join. Here is the demo.
select
s.ID,
Num_Part,
Description,
Location,
Stock
from Stock s
left join Parts p
on s.ID = p.ID
order by
s.ID
output:
| id | num_part | description | location | stock |
| --- | -------- | ----------- | -------- | ----- |
| 1 | sr37 | plate | A | 2 |
| 2 | sr56 | punch | NULL | NULL |
| 3 | sl30 | crimper | B | 5 |
| 4 | mp11 | holder | NULL | NULL |

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

How to create a query for items that appears only in one list

The scenario I am working on is as follows:-
A number of interviews are conducted a food poisoning case
A query called qryFoodInCase ( fldCaseID, fldFood) is generate consisting of all the food mentioned in all the interviews
An other query call qryFoodInInterview( fldCaseID, fldInterviewID, fldFood) consist of the food mentioned in each Interview
Now I am after the sql for a query that will return the food that was not consumed by an interviewee but consumed by one or more other interviewees.
The closest I've got is:
select Q1.fldCaseID,Q1.fldfood,Q2.fldInterviewID,fldGotSick
from qryFoodInCases as Q1
left join
(select * from qryFoodInInterview where qryFoodInInterview.fldInterviewID=1) as Q2
on Q1.fldFood=Q2.fldFood
where Q1.fldCaseID=1
The field Q2.fldInterviewID returns 1 for the food consumed and null for the food not consumed. However, I don't want to hard code the fldInterviewID in the sql. I would like a similar recordset returned for all the interviews in one query.
The SQL for qryFoodInCase and qryFoodInInterview are as follows:-
CREATE VIEW `qryFoodInCases`
AS
SELECT tblCases.fldCaseID
,fldfood
,count(tblFoodHistory.fldFoodID) AS fldFoodFrequency
FROM tblFood
INNER JOIN tblFoodHistory
ON tblFoodHistory.fldFoodID = tblFood.fldFoodID
INNER JOIN tblMealHistory
ON tblFoodHistory.fldMealID = tblMealHistory.fldMealHistoryID
INNER JOIN tblInterviews
ON tblInterviews.fldInterviewID = tblMealHistory.fldInterviewID
INNER JOIN tblCases
ON tblCases.fldCaseID = tblInterviews.fldCaseID
GROUP BY tblCases.fldCaseID, tblFood.fldFood
OUTPUT:
+-----------+------------+------------------+
| fldCaseID | fldFood | fldFoodFrequency |
+-----------+------------+------------------+
| 1 | Banana | 3 |
| 1 | Beans | 5 |
| 1 | Cabagge | 3 |
| 1 | Chicken | 1 |
| 1 | Pork | 5 |
| 1 | Potatoes | 1 |
| 1 | Rice | 1 |
| 1 | fried fish | 1 |
| 2 | Cabagge | 1 |
| 2 | Chicken | 2 |
| 2 | Potatoes | 1 |
| 2 | Rice | 1 |
| 2 | Salad | 1 |
+-----------+------------+------------------+
and
CREATE VIEW `qryFoodInInterview`
AS
SELECT tblInterviews.fldCaseID
,tblInterviews.fldInterviewID
,tblFood.fldFood
,tblInterviews.fldGotSick
FROM tblInterviews
INNER JOIN tblMealHistory
ON tblInterviews.fldInterviewID = tblMealHistory.fldInterviewID
INNER JOIN tblFoodHistory
ON tblFoodHistory.fldMealID = tblMealHistory.fldMealHistoryID
INNER JOIN tblFood
ON tblFood.fldFoodID = tblFoodHistory.fldFoodID
GROUP BY tblInterviews.fldInterviewID, tblFoodHistory.fldFoodID
OUTPUT
+-----------+----------------+------------+------------+
| fldCaseID | fldInterviewID | fldFood | fldGotSick |
+-----------+----------------+------------+------------+
| 1 | 1 | Pork | 0 |
| 1 | 1 | Banana | 0 |
| 1 | 1 | Rice | 0 |
| 1 | 1 | Potatoes | 0 |
| 1 | 2 | Chicken | 1 |
| 1 | 2 | Banana | 1 |
| 1 | 2 | Beans | 1 |
| 1 | 4 | Pork | 1 |
| 1 | 4 | fried fish | 1 |
| 1 | 4 | Beans | 1 |
| 2 | 6 | Salad | 0 |
| 2 | 6 | Chicken | 0 |
| 2 | 6 | Cabagge | 0 |
| 2 | 6 | Rice | 0 |
| 2 | 6 | Potatoes | 0 |
| 1 | 8 | Pork | 0 |
| 1 | 8 | Cabagge | 0 |
| 1 | 9 | Pork | 1 |
| 1 | 9 | Banana | 1 |
| 1 | 9 | Beans | 1 |
| 1 | 10 | Cabagge | 1 |
| 1 | 10 | Beans | 1 |
| 1 | 11 | Pork | 1 |
| 1 | 11 | Cabagge | 1 |
| 1 | 11 | Beans | 1 |
+-----------+----------------+------------+------------+
SQL Fiddle Demo
create a cross join off all food with all interviewrs
then a left join to see which one didnt had interview
the null mean interview didnt consume food
and exists mean someone different to interview consume food
.
SELECT F.fldFood, I.fldInterviewID, FI.fldInterviewID
FROM qryFoodInCases F, (SELECT DISTINCT fldInterviewID FROM qryFoodInInterview) I
LEFT JOIN qryFoodInInterview FI
ON F.fldFood = FI.fldFood
AND I.fldInterviewID = FI.fldInterviewID
WHERE FI.fldInterviewID IS NULL
AND EXISTS (SELECT 1
FROM qryFoodInInterview Q1
WHERE Q1.fldFood = F.fldFood
AND Q1.fldInterviewID <> I.fldInterviewID)
;
The trick was do the thing one step at the time. Once I realize I need all the combinations and create the cross join, the rest was easy.
select Q1.fldCaseID,Q1.fldfood,Q2.fldInterviewID,fldGotSick
from qryFoodInCases as Q1
left join
(select * from qryFoodInInterview QF1
JOIN qryFoodInInterview QF2
where QF1.fldInterviewID <> QF2.fldInterviewID
AND QF1.fldInterviewID > QF2.fldInterviewID
) as Q2
on Q1.fldFood=Q2.fldFood
where Q1.fldCaseID=1
As you mentioned you want to duplicate the items FoodInterviews, self JOIN will suffice your question. Please try this out , caution I didn't tested it.
Fixing Missed Self Join Condition to fix duplicates.

Sum data from two tables with different number of rows

There are 3 Tables (SorMaster, SorDetail, and InvWarehouse):
SorMaster:
+------------+
| SalesOrder |
+------------+
| 100 |
| 101 |
| 102 |
+------------+
SorDetail:
+------------+------------+---------------+
| SalesOrder | MStockCode | MBackOrderQty |
+------------+------------+---------------+
| 100 | PN-1 | 4 |
| 100 | PN-2 | 9 |
| 100 | PN-3 | 1 |
| 100 | PN-4 | 6 |
| 101 | PN-1 | 6 |
| 101 | PN-3 | 2 |
| 102 | PN-2 | 19 |
| 102 | PN-3 | 14 |
| 102 | PN-4 | 6 |
| 102 | PN-5 | 4 |
+------------+------------+---------------+
InvWarehouse:
+------------+-----------+-----------+
| MStockCode | Warehouse | QtyOnHand |
+------------+-----------+-----------+
| PN-1 | A | 1 |
| PN-2 | B | 9 |
| PN-3 | A | 0 |
| PN-4 | B | 1 |
| PN-1 | A | 0 |
| PN-3 | B | 5 |
| PN-2 | A | 9 |
| PN-3 | B | 4 |
| PN-4 | A | 6 |
| PN-5 | B | 0 |
+------------+-----------+-----------+
Desired Results:
+------------+-----------------+--------------+
| MStockCode | SumBackOrderQty | SumQtyOnHand |
+------------+-----------------+--------------+
| PN-1 | 10 | 10 |
| PN-2 | 28 | 1 |
| PN-3 | 17 | 5 |
| PN-4 | 12 | 13 |
| PN-5 | 11 | 6 |
+------------+-----------------+--------------+
I have been going around in circles with no end in sight. Seems like it should be simple but just can't wrap my head around it. The SumBackOrderQty obviously getting counted twice as the SumQtyOnHand is evaluated. To this point I have been doing the calculations in the PHP instead of the select statement but would like to clean things up a bit where possible.
Current query statement is:
SELECT SorDetail.MStockCode,
SUM(SorDetail.MBackOrderQty) AS 'SumMBackOrderQty',
SUM(InvWarehouse.QtyOnHand) AS 'SumQtyOnHand'
FROM SysproCompanyJ.dbo.SorMaster SorMaster,
SysproCompanyJ.dbo.SorDetail SorDetail LEFT OUTER JOIN SysproCompanyJ.dbo.InvWarehouse InvWarehouse
ON SorDetail.MStockCode = InvWarehouse.StockCode
WHERE SorMaster.SalesOrder = SorDetail.SalesOrder
AND SorMaster.ActiveFlag != 'N'
AND SorDetail.MBackOrderQty > '0'
AND SorDetail.MPrice > '0'
GROUP BY SorDetail.MStockCode
ORDER BY SorDetail.MStockCode ASC
Without providing the complete picture, in terms of your RDBMS, database schema, a description of the problem you're trying to solve and sample data that matches the aforementioned, the following is just an illustration of what a solution based on Barmar's comment could look like:
SELECT SD.MStockCode,
SD.SumBackOrderQty,
IW.SumQtyOnHand
FROM (SELECT MStockCode,
SUM(MBackOrderQty) AS `SumBackOrderQty`
FROM SorDetail
JOIN SorMaster ON SorDetail.SalesOrder=SorMaster.SalesOrder
WHERE SorMaster.ActiveFlag != 'N'
AND SorDetail.MBackOrderQty > 0
AND SorDetail.MPrice > 0
GROUP BY MStockCode) AS SD
LEFT JOIN (SELECT MStockCode,
SUM(QtyOnHand) AS `SumQtyOnHand`
FROM InvWarehouse
GROUP BY MStockCode) AS IW ON SD.MStockCode=IW.MStockCode
ORDER BY SD.MStockCode;
Here's one approach:
select MStockCode,
(select sum(MBackOrderQty) from sorDetail as T2
where T2.MStockCode = T1.MStockCode ) as SumBackOrderQty,
(select sum(QtyOnHand) from invWarehouse as T3
where T3.MStockCode = T1.MStockCode ) as SumQtyOnHand
from
(
select mstockcode from sorDetail
union
select mstockcode from invWarehouse
) as T1
In a fiddle here: http://sqlfiddle.com/#!9/fdaca/6
Though my SumQtyOnHand values don't match yours (as #Gordon pointed out).