Cross join, display table 2 column value base on table1 - sql-server

I have two tables in SQL Server, Say in table1 I have two columns Key1Display and Key2Display, they are of datatype bit and used to control whether to display the values in table2, and table 2 will have 2 columns Key1 and Key2.
What I am trying to achieve is a sort of cross join, say if table 1 has 3 rows:
| Key1Display | Key2Display |
+---------------------+------------------+
| 0 | 1 |
| 1 | 0 |
| 1 | 1 |
Say in table 2 there are 2 rows
| Key1 | Key2 |
+---------------------+------------------+
| Row1Key1value | Row1Key2value |
| Row2Key1value | Row2Key2value |
Then based on these two tables, I want to have a query to display 6 (2*3) rows and 1 column of results like this:
null:Row1Key2value
Row1Key1Value:null
Row1Key1Value:Row1Key2value
null:Row2Key2value
Row1Key2Value:null
Row1Key2Value:Row2Key2value

So something like:
select
case when t1.Key1Display = 1 then coalesce(t2.Key1,'??') else 'null' end
+ ':' + case when t1.Key2Display = 1 then coalesce(t2.Key2,'??') else 'null' end
-- And so on for as many keys as you have
from table1 t1
cross join table2 t2

Related

Update hierarchy after deletion of row

I have a table that contains tree-like data (hierarchic design). Here is a small sample:
+----+----------+-----------+-------+----------+---------+
| ID | ParentID | Hierarchy | Order | FullPath | Project |
+----+----------+-----------+-------+----------+---------+
| 1 | null | 1 | 1 | 1 | 1 |
| 2 | null | 2 | 2 | 2 | 1 |
| 3 | 1 | 1.1 | 1 | 1-3 | 1 |
| 4 | 1 | 1.2 | 2 | 1-4 | 1 |
| 5 | 4 | 1.2.1 | 1 | 1-4-5 | 1 |
| 6 | 2 | 2.1 | 1 | 2-6 | 1 |
| 7 | null | 3 | 1 | 1 | 2 |
+----+----------+-----------+-------+----------+---------+
Project indicates which project owns the hierarchic dataset
ParentID is the ID of the parent node, it has a foreign key on ID.
Order is the rank of the element in one branch. For example, IDs 1, 2 and 7 are on the same node while 3 and 4 are in another.
FullPath shows the order using the ID (it's for system use and performance reasons).
Hierarchy is the column displayed to the user, which displays the hierarchy to the UI. It auto calculates after every insert, update and delete, and it's the one I'm having issues.
I created a procedure for deletion elements in the table. It receives as input the ID of the element to delete and deletes it, along with it's children if any. Then, it recalculates the FullPath and the Order Column .That works.
Problems is when I try to update the Hierarchy column. I use this procedure:
SELECT T.ID,
T.ParentID,
CASE WHEN T.ParentID IS NOT NULL THEN
CONCAT(T1.Hierarchy, '.', CAST(T.Order AS NVARCHAR(255)))
ELSE
CAST(T.Order AS NVARCHAR(255))
END AS Hierarchy
INTO #tmp
FROM t_HierarchyTable T
LEFT JOIN t_HierarchyTable T1
ON T1.ID = T.ParentID
WHERE Project = #Project --Variable to only update the current project for performance
ORDER BY T.FullPath
--Update the table with ID as key on tmp table
This fails when I delete items that have lower order than others and they have children.
For example, if I delete the item 3, item 4 Hierachy will be corrected (1.1), BUT its child won't (it will stay at 1.2.1, while it should be 1.1.1). I added the order by to make sure parents where updated first, but no change.
What is my error, I really don't know how to fix this.
I managed to update the hierarchy with a CTE. Since I have the order, I can append it to Hierarchy, based on the previous branch (parent) who is already updated.
;WITH CODES(ID, sCode, iLevel) AS
(
SELECT
T.[ID] AS [ID],
CONVERT(VARCHAR(8000), T.[Order]) AS [Hierarchy],
1 AS [iLevel]
FROM
[dbo].[data] AS T
WHERE
T.[ParentID] IS NULL
UNION ALL
SELECT
T.[ID] AS [ID],
P.[Hierarchy] + IIF(RIGHT(P.[Hierarchy], 1) <> '-', '-', '') + CONVERT(VARCHAR(8000), T.[Order]) AS [Hierarchy],
P.[iLevel] + 1 AS [iLevel]
FROM
[dbo].[data] AS T
INNER JOIN CODES AS P ON
P.[ID] = T.[ParentID]
WHERE
P.[iLevel] < 100
)
SELECT
[ID], [Hierarchy], [iLevel]
INTO
#CODES
FROM
CODES

Using Where clause in Case to compare

I am new to T-sql .
I am using the following Query:
SELECT e.Id,e.cAvg,
CASE
WHEN e.cAvg<=0.8 and cAvg>=0 THEN t.Model when t.Cr='0.8' then t.Model
WHEN e.cAvg>0.8 and cAvg<=5.4 THEN t.Model WHEN t.Cr='5.4' then t.Model
WHEN e.cAvg>5.4 and cg<=8 THEN t.Model WHEN t.Cr='8' then t.Model
ELSE 'No Change Required'
END
from A e, B t;
What I am trying to do is:
Select id and cAvg columns in Table A.
Compare cAvg in Table A with Cr in Table B.
Use the comparison in CASE to select the particular row which satisfies the condition.
Use the selected row to give query results.
t.Model is a column of table B. I want to select t.Model value of the selected row in the case statement.
I feel the way is to somehow include a equivalent of the where clause inside When of CASE.
Need Direction!!
The table schema:
Table A:
+----+------+
| id | cAvg |
+----+------+
| 1 | .8 |
| 2 | 5.4 |
| 3 | 6.0 |
+----+------+
Table B:
+-----+-------+
| Cr | Model |
+-----+-------+
| 2 | M1 |
| 5.5 | M2 |
| 8 | M3 |
+-----+-------+
I want to the following:
Compare the values of cAvg with a condition => (cAvg<=8 And cAvg>=5.5 => the model selected must be M3.)
The result I want to get is:
+----+------+-------+
| id | cAvg | Model |
+----+------+-------+
| 1 | .8 | M1 |
| 2 | 5.4 | M2 |
| 3 | 6.0 | M3 |
+----+------+-------+
I tired Join as suggested in the comments, A great thanks , I learnt a lot because of it!!.
My problem is that there are no common columns to join.
Also I need to compare the column in one table with that of another table and then give a result based on the comparison.
I referred to many answers in stack overflow but all the answers are for the premise where there is a common column.
I tried the following:
Inner Join
Cases
I need a direction as to which direction I need to go into.
Thank you!!
1st of all, you're selecting from 2 tables but without any link restrictions, so all rows are compared
If there is a matching key between the tables, so only relevant pairs of rows would be compared, it should be used, in a JOIN statement:
A e JOIN B t ON e.id = t.id
2nd of all, in order to select relevant lines, you should decide what these are..
you can inside a WHERE statement define whatvare the relent cases
WHERE
e.cAvg > 12
You can use the case statement inside WHERE but then the result should be conditioned are returned TRUE
SELECT e.Id,e.cAvg, t.Model
A e JOIN B t ON e.id = t.id
WHERE
CASE WHEN e.cAvg<=6 THEN t.Model when t.Cr=6 then t.Model
WHEN e.cAvg>6 and e.cAvg<=12 THEN t.Model
WHEN t.Cr='12' then t.Model
WHEN e.cAvg>12 and cg<=24 THEN t.Model
WHEN t.Cr='24' then t.Model
ELSE -1 END ! = -1
EDIT
Following you question edit, I think that what you need is a JOIN with a condition
Basically, instead of joining the tables with an equal key, join them with an unequal key.
Since you're looking for cAvg BETWEEN to t. Cr rows, 2 JOINs are needed
SELECT e.Id,e.cAvg, t.Model
FROM
A e JOIN B t ON
e.cAvg >= t.Cr
JOIN B t2 ON
e.cAvg < t2.Cr
WHERE
t.Cr IS NOT NULL
AND t2.Cr IS NOT NULL
The idea is that only where the 2 conditions are met, you would get the results of e
Hope that helps
I found a possible work around for the problem.
Problem Statement:
Compare two tables with no common column.
Use the comparison in CASE to select a particular row.
A WHERE Clause inside CASE is not accepted in T-SQL.
My Work Around :
Add a new column in the second table.
Assign An id from Table B to the column in Table A.
Use the assigned id to select the required row in Table B.
Tables:
Table A:
+----+------+
| id | cAvg |
+----+------+
| 1 | .8 |
| 2 | 5.4 |
| 3 | 6.0 |
+----+------+
Table B
+-----+-----+-------+
| Bid | Cr | Model |
+-----+-----+-------+
| 1 | 2 | M1 |
| 2 | 5.5 | M2 |
| 3 | 8 | M3 |
+-----+-----+-------+
Query to assign id's:
CREATE VIEW [AssignIDView] AS
SELECT DISTINCT e.id,
e.cAvg,
(CASE
WHEN e.cAvg>=0 and e.cAvg<=2 THEN 1
WHEN e.cAvg>2 and e.cAvg<=5.5 THEN 2
WHEN e.cAvg>3 and e.cAvg<=8 THEN 3
ELSE 'Invalid'
END) As BId
FROM A e, B t;
The result of the above Query will be a view as follows:
+----+------+-----+
| id | cAvg | Bid |
+----+------+-----+
| 1 | .8 | 1 |
| 2 | 5.4 | 2 |
| 3 | 6.0 | 3 |
+----+------+-----+
Now use Bid to select rows from table B to assign Model from B:
Query:
CREATE VIEW [ModelAssignView] AS
select e.id,
e.cAvg,
t.Model as [Model]
FROM A e, B t where e.TierID = t.id;
The result of the Query will be as follows:
+----+------+-------+
| id | cAvg | Model |
+----+------+-------+
| 1 | .8 | M1 |
| 2 | 5.4 | M2 |
| 3 | 6.0 | M3 |
+----+------+-------+
The intention of my question was to do the above.
For that I wanted to find an Equivalent of A WHERE Clause inside CASE.
But the above method achieved the solution for me.
Hope it helps:)!

Setting 0 when NO rows from select statement with multiple tables

Let's say I have this table
(Simplified, my query have more tables involved)
ConsignmentItem
ID |Item code| Name | Quantity
1 | 00000 | A | 3
2 | 11111 | B | 2
And this other table
PickingItem
ID |ConsignmentID|Quantity
1 | 1 | 1
What my query does is to join both tables and print the amount of products ordered and the amount of products already registered. I would like to get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 0
My query works whenever the item exist on "PickingItem", if it doesn't it prints the same "Quantity_Registered" as the above row, using my query I get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 1(this is wrong)
This is the query i'm using
SELECT C.Barcode AS 'Item Code',C.ProductName AS 'Name', C.Quantity AS 'Quantity_Ordered', ISNULL(P.Quantity,0) AS 'Quantity_Registered'
FROM PICKING.OrderPickingItem P
JOIN PICKING.OrderPicking OP ON P.PickingID = OP.PickingID
JOIN ORDERS.ConsignmentItem C ON OP.ConsignmentID = C.ConsignmentID
WHERE P.PickingID = 1 --For testing
Anyone know what could I do to, if the product doesn't exist on OrderPickingItem, then set P.Quantity = 0 for that specific row?
EDIT:
Structure of the tables
OrderPickingItem
PickingItemID PK
PickingID FK
ConsignmentItemID FK
Quantity
--other not used columns for this query
OrderPicking
PickingID PK
ConsignmentID FK
--other not used columns for this query
ConsignmentItem
ConsignmentItemID PK
ConsignmentID FK
Barcode
Quantity
ProductName
--other not used columns for this query
You are obviously looking for an outer join:, you want to show ConsignmentItem records even when there is no matching picking.
select
C.Barcode AS "Item Code",
C.ProductName AS "Name",
C.Quantity AS "Quantity_Ordered",
ISNULL(P.Quantity, 0) AS "Quantity_Registered"
from ORDERS.ConsignmentItem c
left join PICKING.OrderPicking op on OP.ConsignmentID = C.ConsignmentID
left join PICKING.OrderPickingItem P on P.PickingID = OP.PickingID
and P.ConsignmentItemID = C.ConsignmentItemID;
Do an outer join and something like ifnull(p.id, 0) for the quantity in the select clause.

SQL Query to get data based on multiple filters

I have following Product table and ProductTag tables -
ID | Product
--------------
1 | Product_A
2 | Product_B
3 | Product_C
TagID | ProductID
----------------------
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
Now I need a SQL query that return all products list which are having both Tag 1 and 2. Result should be as given below -
ProductID | Product
------------------------
2 | Product_B
3 | Product_C
Please suggest how can i write a MS SQL query for this.
SELECT p.ID, p.Product
FROM Product p
INNER JOIN ProductTag pt
ON p.ID = pt.ProductID
WHERE pt.TagID IN (1, 2) -- <== Tags you want to find
GROUP BY p.ID, o.Product
HAVING COUNT(*) = 2 -- <== tag count on WHERE clause
however, if TagID is not unique on every Product, you need to count only the distinct product.
HAVING COUNT(DISTINCT pt.TagID) = 2
More on: SQL of Relational Division

Select column based on whether a specific row in another table exists

Question is similar to this one How to write a MySQL query that returns a temporary column containing flags for whether or not an item related to that row exists in another table
Except that I need to be more specific about which rows exists
I have two tables: 'competitions' and 'competition_entries'
Competitions:
ID | NAME | TYPE
--------------------------------
1 | Example | example type
2 | Another | example type
Competition Entries
ID | USERID | COMPETITIONID
---------------------------------
1 | 100 | 1
2 | 110 | 1
3 | 110 | 2
4 | 120 | 1
I want to select the competitions but add an additional column which specifies whether the user has entered the competition or not. This is my current SELECT statement
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e
ON e.COMPETITIONID = c.ID
My desired result set from setting the #userid parameter to 110 is this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 1
2 | Another | example type | 1
But instead I get this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 0
1 | Example | example type | 1
1 | Example | example type | 0
2 | Another | example type | 1
Because it's counting the entries for all user ids
Fixing your query
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
MAX(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e ON e.COMPETITIONID = c.ID
GROUP BY
c.[ID],
c.[NAME],
c.[TYPE]
An alternative is to rewrite it using EXISTS which is pretty much the same but may be easier to understand.
BTW, using single quotes on the column name is deprecated. Use square brackets.
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
CASE WHEN EXISTS (
SELECT *
FROM competition_entries AS e
WHERE e.COMPETITIONID = c.ID
AND e.USERID = #userid) THEN 1 ELSE 0 END [ENTERED]
FROM competitions AS c

Resources